Merge "Preserve verification invariant after compile-time verification."
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index abd35f1..02b26c6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2650,6 +2650,10 @@
                                                             dex_class_def,
                                                             &new_dex_file,
                                                             &new_class_def);
+  // Check to see if an exception happened during runtime callbacks. Return if so.
+  if (self->IsExceptionPending()) {
+    return nullptr;
+  }
   ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
   if (dex_cache == nullptr) {
     self->AssertPendingOOMException();
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a5db0c0..f56226b 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -207,6 +207,19 @@
 }
 
 template<typename T>
+inline PrimitiveArray<T>* PrimitiveArray<T>::AllocateAndFill(Thread* self,
+                                                             const T* data,
+                                                             size_t length) {
+  StackHandleScope<1> hs(self);
+  Handle<PrimitiveArray<T>> arr(hs.NewHandle(PrimitiveArray<T>::Alloc(self, length)));
+  if (!arr.IsNull()) {
+    // Copy it in. Just skip if it's null
+    memcpy(arr->GetData(), data, sizeof(T) * length);
+  }
+  return arr.Get();
+}
+
+template<typename T>
 inline PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
   Array* raw_array = Array::Alloc<true>(self,
                                         GetArrayClass(),
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 19d300e..16cf30f 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -119,6 +119,10 @@
   static PrimitiveArray<T>* Alloc(Thread* self, size_t length)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
+  static PrimitiveArray<T>* AllocateAndFill(Thread* self, const T* data, size_t length)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+
   const T* GetData() const ALWAYS_INLINE  REQUIRES_SHARED(Locks::mutator_lock_) {
     return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
   }
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 256c3a6..91353e2 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -114,6 +114,21 @@
 }
 
 ALWAYS_INLINE
+static inline jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
+                                                 const unsigned char* source,
+                                                 jint len,
+                                                 /*out*/unsigned char** dest) {
+  jvmtiError res = env->Allocate(len, dest);
+  if (res != OK) {
+    return res;
+  }
+  memcpy(reinterpret_cast<void*>(*dest),
+         reinterpret_cast<const void*>(source),
+         len);
+  return OK;
+}
+
+ALWAYS_INLINE
 static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
   size_t len = strlen(src) + 1;
   unsigned char* buf;
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 655a53a..4f5eb0c 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -131,8 +131,8 @@
   ArtJvmTiEnv* last_env = nullptr;
   for (ArtJvmTiEnv* env : envs) {
     if (ShouldDispatch<kEvent>(env, thread)) {
-      jint new_len;
-      unsigned char* new_data;
+      jint new_len = 0;
+      unsigned char* new_data = nullptr;
       auto callback = impl::GetCallback<kEvent>(env);
       callback(env,
                jnienv,
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 6dbab86..b6de592 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -31,6 +31,8 @@
 
 #include "ti_class.h"
 
+#include "android-base/stringprintf.h"
+
 #include <mutex>
 #include <unordered_set>
 
@@ -38,20 +40,208 @@
 #include "base/macros.h"
 #include "class_table-inl.h"
 #include "class_linker.h"
+#include "common_throws.h"
 #include "events-inl.h"
 #include "handle.h"
 #include "jni_env_ext-inl.h"
 #include "jni_internal.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_ext.h"
 #include "runtime.h"
 #include "runtime_callbacks.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
+#include "ti_redefine.h"
+#include "utils.h"
 
 namespace openjdkjvmti {
 
+using android::base::StringPrintf;
+
+static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self,
+                                                             const char* descriptor,
+                                                             const std::string& orig_location,
+                                                             jint final_len,
+                                                             const unsigned char* final_dex_data)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  // Make the mmap
+  std::string error_msg;
+  std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location,
+                                                               final_len,
+                                                               final_dex_data,
+                                                               &error_msg));
+  if (map.get() == nullptr) {
+    LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
+    self->ThrowOutOfMemoryError(StringPrintf(
+        "Unable to allocate dex file for transformation of %s", descriptor).c_str());
+    return nullptr;
+  }
+
+  // Make a dex-file
+  if (map->Size() < sizeof(art::DexFile::Header)) {
+    LOG(WARNING) << "Could not read dex file header because dex_data was too short";
+    art::ThrowClassFormatError(nullptr,
+                               "Unable to read transformed dex file of %s",
+                               descriptor);
+    return nullptr;
+  }
+  uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
+  std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map->GetName(),
+                                                                  checksum,
+                                                                  std::move(map),
+                                                                  /*verify*/true,
+                                                                  /*verify_checksum*/true,
+                                                                  &error_msg));
+  if (dex_file.get() == nullptr) {
+    LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg;
+    art::ThrowClassFormatError(nullptr,
+                               "Unable to read transformed dex file of %s because %s",
+                               descriptor,
+                               error_msg.c_str());
+    return nullptr;
+  }
+  if (dex_file->NumClassDefs() != 1) {
+    LOG(WARNING) << "Dex file contains more than 1 class_def. Ignoring.";
+    // TODO Throw some other sort of error here maybe?
+    art::ThrowClassFormatError(
+        nullptr,
+        "Unable to use transformed dex file of %s because it contained too many classes",
+        descriptor);
+    return nullptr;
+  }
+  return dex_file;
+}
+
 struct ClassCallback : public art::ClassLoadCallback {
+  void ClassPreDefine(const char* descriptor,
+                      art::Handle<art::mirror::Class> klass,
+                      art::Handle<art::mirror::ClassLoader> class_loader,
+                      const art::DexFile& initial_dex_file,
+                      const art::DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+                      /*out*/art::DexFile const** final_dex_file,
+                      /*out*/art::DexFile::ClassDef const** final_class_def)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    bool is_enabled =
+        event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookRetransformable) ||
+        event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+    if (!is_enabled) {
+      return;
+    }
+    if (descriptor[0] != 'L') {
+      // It is a primitive or array. Just return
+      return;
+    }
+    std::string name(art::PrettyDescriptor(descriptor));
+
+    art::Thread* self = art::Thread::Current();
+    art::JNIEnvExt* env = self->GetJniEnv();
+    ScopedLocalRef<jobject> loader(
+        env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get()));
+    // Go back to native.
+    art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
+    // Call all Non-retransformable agents.
+    jint post_no_redefine_len = 0;
+    unsigned char* post_no_redefine_dex_data = nullptr;
+    std::unique_ptr<const unsigned char> post_no_redefine_unique_ptr(nullptr);
+    event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+        self,
+        static_cast<JNIEnv*>(env),
+        static_cast<jclass>(nullptr),  // The class doesn't really exist yet so send null.
+        loader.get(),
+        name.c_str(),
+        static_cast<jobject>(nullptr),  // Android doesn't seem to have protection domains
+        static_cast<jint>(initial_dex_file.Size()),
+        static_cast<const unsigned char*>(initial_dex_file.Begin()),
+        static_cast<jint*>(&post_no_redefine_len),
+        static_cast<unsigned char**>(&post_no_redefine_dex_data));
+    if (post_no_redefine_dex_data == nullptr) {
+      DCHECK_EQ(post_no_redefine_len, 0);
+      post_no_redefine_dex_data = const_cast<unsigned char*>(initial_dex_file.Begin());
+      post_no_redefine_len = initial_dex_file.Size();
+    } else {
+      post_no_redefine_unique_ptr = std::unique_ptr<const unsigned char>(post_no_redefine_dex_data);
+      DCHECK_GT(post_no_redefine_len, 0);
+    }
+    // Call all retransformable agents.
+    jint final_len = 0;
+    unsigned char* final_dex_data = nullptr;
+    std::unique_ptr<const unsigned char> final_dex_unique_ptr(nullptr);
+    event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+        self,
+        static_cast<JNIEnv*>(env),
+        static_cast<jclass>(nullptr),  // The class doesn't really exist yet so send null.
+        loader.get(),
+        name.c_str(),
+        static_cast<jobject>(nullptr),  // Android doesn't seem to have protection domains
+        static_cast<jint>(post_no_redefine_len),
+        static_cast<const unsigned char*>(post_no_redefine_dex_data),
+        static_cast<jint*>(&final_len),
+        static_cast<unsigned char**>(&final_dex_data));
+    if (final_dex_data == nullptr) {
+      DCHECK_EQ(final_len, 0);
+      final_dex_data = post_no_redefine_dex_data;
+      final_len = post_no_redefine_len;
+    } else {
+      final_dex_unique_ptr = std::unique_ptr<const unsigned char>(final_dex_data);
+      DCHECK_GT(final_len, 0);
+    }
+
+    if (final_dex_data != initial_dex_file.Begin()) {
+      LOG(WARNING) << "Changing class " << descriptor;
+      art::ScopedObjectAccess soa(self);
+      art::StackHandleScope<2> hs(self);
+      // Save the results of all the non-retransformable agents.
+      // First allocate the ClassExt
+      art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(self)));
+      // Make sure we have a ClassExt. This is fine even though we are a temporary since it will
+      // get copied.
+      if (ext.IsNull()) {
+        // We will just return failure if we fail to allocate
+        LOG(WARNING) << "Could not allocate ext-data for class '" << descriptor << "'. "
+                     << "Aborting transformation since we will be unable to store it.";
+        self->AssertPendingOOMException();
+        return;
+      }
+
+      // Allocate the byte array to store the dex file bytes in.
+      art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
+          art::mirror::ByteArray::AllocateAndFill(
+              self,
+              reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
+              post_no_redefine_len)));
+      if (arr.IsNull()) {
+        LOG(WARNING) << "Unable to allocate byte array for initial dex-file bytes. Aborting "
+                     << "transformation";
+        self->AssertPendingOOMException();
+        return;
+      }
+
+      std::unique_ptr<const art::DexFile> dex_file(MakeSingleDexFile(self,
+                                                                     descriptor,
+                                                                     initial_dex_file.GetLocation(),
+                                                                     final_len,
+                                                                     final_dex_data));
+      if (dex_file.get() == nullptr) {
+        return;
+      }
+
+      // TODO Check Redefined dex file for invariants.
+      LOG(WARNING) << "Dex file created by class-definition time transformation of "
+                   << descriptor << " is not checked for all retransformation invariants.";
+      // TODO Put it in classpath
+      LOG(WARNING) << "Dex file created for class-definition time transformation of "
+                   << descriptor << " was not added to any classpaths!";
+      // Actually set the ClassExt's original bytes once we have actually succeeded.
+      ext->SetOriginalDexFileBytes(arr.Get());
+      // Set the return values
+      *final_class_def = &dex_file->GetClassDef(0);
+      *final_dex_file = dex_file.release();
+    }
+  }
+
   void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
     if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) {
       art::Thread* thread = art::Thread::Current();
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 34efc50..d2ddc21 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -520,28 +520,13 @@
   result_ = result;
 }
 
-// Allocates a ByteArray big enough to store the given number of bytes and copies them from the
-// bytes pointer.
-static art::mirror::ByteArray* AllocateAndFillBytes(art::Thread* self,
-                                                    const uint8_t* bytes,
-                                                    int32_t num_bytes)
-    REQUIRES_SHARED(art::Locks::mutator_lock_) {
-  art::StackHandleScope<1> hs(self);
-  art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
-      art::mirror::ByteArray::Alloc(self, num_bytes)));
-  if (!arr.IsNull()) {
-    // Copy it in. Just skip if it's null
-    memcpy(arr->GetData(), bytes, num_bytes);
-  }
-  return arr.Get();
-}
-
 art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() {
   // If we have been specifically given a new set of bytes use that
   if (original_dex_file_.size() != 0) {
-    return AllocateAndFillBytes(driver_->self_,
-                                &original_dex_file_.At(0),
-                                original_dex_file_.size());
+    return art::mirror::ByteArray::AllocateAndFill(
+        driver_->self_,
+        reinterpret_cast<const signed char*>(&original_dex_file_.At(0)),
+        original_dex_file_.size());
   }
 
   // See if we already have one set.
@@ -561,7 +546,10 @@
     LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses "
                  << "on this class might fail if no transformations are applied to it!";
   }
-  return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size());
+  return art::mirror::ByteArray::AllocateAndFill(
+      driver_->self_,
+      reinterpret_cast<const signed char*>(current_dex_file.Begin()),
+      current_dex_file.Size());
 }
 
 struct CallbackCtx {
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 29a7e1f..fdc13ee 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -96,6 +96,11 @@
 
   static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
 
+  static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
+                                                       jint data_len,
+                                                       const unsigned char* dex_data,
+                                                       std::string* error_msg);
+
  private:
   class ClassRedefinition {
    public:
@@ -234,11 +239,6 @@
                                               /*out*/std::string* error_msg)
       REQUIRES_SHARED(art::Locks::mutator_lock_);
 
-  static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
-                                                       jint data_len,
-                                                       const unsigned char* dex_data,
-                                                       std::string* error_msg);
-
   // TODO Put on all the lock qualifiers.
   jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_);
 
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index 745c0f5..3c4cfea 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -137,20 +137,6 @@
   return OK;
 }
 
-static jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
-                                          const unsigned char* source,
-                                          jint len,
-                                          /*out*/unsigned char** dest) {
-  jvmtiError res = env->Allocate(len, dest);
-  if (res != OK) {
-    return res;
-  }
-  memcpy(reinterpret_cast<void*>(*dest),
-         reinterpret_cast<const void*>(source),
-         len);
-  return OK;
-}
-
 jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
                                                       art::Handle<art::mirror::Class> klass,
                                                       /*out*/jint* dex_data_len,
diff --git a/test/934-load-transform/build b/test/934-load-transform/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/934-load-transform/build
@@ -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-build "$@" --experimental agents
diff --git a/test/934-load-transform/expected.txt b/test/934-load-transform/expected.txt
new file mode 100644
index 0000000..2b60207
--- /dev/null
+++ b/test/934-load-transform/expected.txt
@@ -0,0 +1 @@
+Goodbye
diff --git a/test/934-load-transform/info.txt b/test/934-load-transform/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/934-load-transform/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/934-load-transform/run b/test/934-load-transform/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/934-load-transform/run
@@ -0,0 +1,19 @@
+#!/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 "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/934-load-transform/src/Main.java b/test/934-load-transform/src/Main.java
new file mode 100644
index 0000000..0b7f2689
--- /dev/null
+++ b/test/934-load-transform/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.util.Base64;
+public class Main {
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
+    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+  public static void main(String[] args) {
+    doTest();
+  }
+
+  public static void doTest() {
+    addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
+    enableCommonRetransformation(true);
+    new Transform().sayHi();
+  }
+
+  // Transforms the class
+  private static native void enableCommonRetransformation(boolean enable);
+  private static native void addCommonTransformationResult(String target_name,
+                                                           byte[] class_bytes,
+                                                           byte[] dex_bytes);
+}
diff --git a/test/934-load-transform/src/Transform.java b/test/934-load-transform/src/Transform.java
new file mode 100644
index 0000000..f624c3a
--- /dev/null
+++ b/test/934-load-transform/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 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.
+ */
+
+class Transform {
+  public void sayHi() {
+    throw new Error("Should not be called!");
+  }
+}
diff --git a/test/935-non-retransformable/build b/test/935-non-retransformable/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/935-non-retransformable/build
@@ -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-build "$@" --experimental agents
diff --git a/test/935-non-retransformable/expected.txt b/test/935-non-retransformable/expected.txt
new file mode 100644
index 0000000..ccd50a6
--- /dev/null
+++ b/test/935-non-retransformable/expected.txt
@@ -0,0 +1,6 @@
+Hello
+Hello
+Goodbye
+Hello
+Hello
+Goodbye
diff --git a/test/935-non-retransformable/info.txt b/test/935-non-retransformable/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/935-non-retransformable/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/935-non-retransformable/run b/test/935-non-retransformable/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/935-non-retransformable/run
@@ -0,0 +1,19 @@
+#!/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 "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/935-non-retransformable/src/Main.java b/test/935-non-retransformable/src/Main.java
new file mode 100644
index 0000000..d9cc329
--- /dev/null
+++ b/test/935-non-retransformable/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform {
+   *   public void sayHi() {
+   *     System.out.println("Hello");
+   *   }
+   *   public void sayGoodbye() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHwoABwAQCQARABIIABMKABQAFQgAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENv" +
+    "ZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEACnNheUdvb2RieWUBAApTb3VyY2VGaWxlAQAO" +
+    "VHJhbnNmb3JtLmphdmEMAAgACQcAGQwAGgAbAQAFSGVsbG8HABwMAB0AHgEAB0dvb2RieWUBAAlU" +
+    "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+    "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+    "YXZhL2xhbmcvU3RyaW5nOylWACAABgAHAAAAAAADAAAACAAJAAEACgAAAB0AAQABAAAABSq3AAGx" +
+    "AAAAAQALAAAABgABAAAAAQABAAwACQABAAoAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAsAAAAK" +
+    "AAIAAAADAAgABAABAA0ACQABAAoAAAAlAAIAAQAAAAmyAAISBbYABLEAAAABAAsAAAAKAAIAAAAG" +
+    "AAgABwABAA4AAAACAA8=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQDpaN+7jX/ZLl9Jr0HAEV7nqL1YDuakKakgAwAAcAAAAHhWNBIAAAAAAAAAAIACAAAQ" +
+    "AAAAcAAAAAYAAACwAAAAAgAAAMgAAAABAAAA4AAAAAUAAADoAAAAAQAAABABAADwAQAAMAEAAJYB" +
+    "AACeAQAApwEAAK4BAAC7AQAA0gEAAOYBAAD6AQAADgIAAB4CAAAhAgAAJQIAADkCAAA+AgAARwIA" +
+    "AFMCAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABQAAAAAAAAAKAAAABQAAAJABAAAEAAEA" +
+    "DAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAADwAAAAEAAQANAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAA" +
+    "AAAACAAAAAAAAABrAgAAAAAAAAEAAQABAAAAWgIAAAQAAABwEAQAAAAOAAMAAQACAAAAXwIAAAkA" +
+    "AABiAAAAGwEBAAAAbiADABAADgAAAAMAAQACAAAAZQIAAAkAAABiAAAAGwECAAAAbiADABAADgAA" +
+    "AAEAAAADAAY8aW5pdD4AB0dvb2RieWUABUhlbGxvAAtMVHJhbnNmb3JtOwAVTGphdmEvaW8vUHJp" +
+    "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
+    "bGFuZy9TeXN0ZW07AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMgAD" +
+    "b3V0AAdwcmludGxuAApzYXlHb29kYnllAAVzYXlIaQABAAcOAAYABw6HAAMABw6HAAAAAQIAgIAE" +
+    "sAIBAcgCAQHsAgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEAAAAHAAAAACAAAABgAAALAAAAADAAAA" +
+    "AgAAAMgAAAAEAAAAAQAAAOAAAAAFAAAABQAAAOgAAAAGAAAAAQAAABABAAABIAAAAwAAADABAAAB" +
+    "EAAAAQAAAJABAAACIAAAEAAAAJYBAAADIAAAAwAAAFoCAAAAIAAAAQAAAGsCAAAAEAAAAQAAAIAC" +
+    "AAA=");
+
+  public static void main(String[] args) {
+    doTest();
+  }
+
+  public static void doTest() {
+    addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
+    enableCommonRetransformation(true);
+    // Actually load the class.
+    Transform t = new Transform();
+    try {
+      // Call functions with reflection. Since the sayGoodbye function does not exist in the
+      // LTransform; when we compile this for the first time we need to use reflection.
+      Method hi = Transform.class.getMethod("sayHi");
+      Method bye = Transform.class.getMethod("sayGoodbye");
+      hi.invoke(t);
+      t.sayHi();
+      bye.invoke(t);
+      // Make sure we don't get called for transformation again.
+      addCommonTransformationResult("Transform", new byte[0], new byte[0]);
+      doCommonClassRetransformation(Transform.class);
+      hi.invoke(t);
+      t.sayHi();
+      bye.invoke(t);
+    } catch (Exception e) {
+      System.out.println("Unexpected error occured! " + e.toString());
+      e.printStackTrace();
+    }
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRetransformation(Class<?>... klasses);
+  private static native void enableCommonRetransformation(boolean enable);
+  private static native void addCommonTransformationResult(String target_name,
+                                                           byte[] class_bytes,
+                                                           byte[] dex_bytes);
+}
diff --git a/test/935-non-retransformable/src/Transform.java b/test/935-non-retransformable/src/Transform.java
new file mode 100644
index 0000000..f624c3a
--- /dev/null
+++ b/test/935-non-retransformable/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 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.
+ */
+
+class Transform {
+  public void sayHi() {
+    throw new Error("Should not be called!");
+  }
+}
diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt
index 383ccd9..05b80e7 100644
--- a/test/957-methodhandle-transforms/expected.txt
+++ b/test/957-methodhandle-transforms/expected.txt
@@ -50,3 +50,12 @@
 a: a, b:b, c: c
 a: a, b:b, c: c
 a: a, b:43
+a: a, b:b, c: c
+a: a, b:true, c: false
+a: a, b:1, c: 2
+a: a, b:a, c: b
+a: a, b:3, c: 4
+a: a, b:42, c: 43
+a: a, b:100, c: 99
+a: a, b:8.9, c: 9.1
+a: a, b:6.7, c: 7.8
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index d0bd816..4035857 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -37,6 +37,7 @@
     testSpreaders_reference();
     testSpreaders_primitive();
     testInvokeWithArguments();
+    testAsCollector();
   }
 
   public static void testThrowException() throws Throwable {
@@ -1240,6 +1241,7 @@
       handle.invokeWithArguments(new Object[] { "a", "b" });
       fail();
     } catch (IllegalArgumentException expected) {
+    } catch (WrongMethodTypeException expected) {
     }
 
     // Test implicit unboxing.
@@ -1252,6 +1254,126 @@
     assertEquals(43, (int) ret);
   }
 
+  public static int collectBoolean(String a, boolean[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 44;
+  }
+
+  public static int collectByte(String a, byte[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 45;
+  }
+
+  public static int collectChar(String a, char[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 46;
+  }
+
+  public static int collectShort(String a, short[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 47;
+  }
+
+  public static int collectInt(String a, int[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 48;
+  }
+
+  public static int collectLong(String a, long[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 49;
+  }
+
+  public static int collectFloat(String a, float[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 50;
+  }
+
+  public static int collectDouble(String a, double[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 51;
+  }
+
+  public static int collectCharSequence(String a, CharSequence[] b) {
+    System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
+    return 99;
+  }
+
+  public static void testAsCollector() throws Throwable {
+    // Reference arrays.
+    // -------------------
+    MethodHandle trailingRef = MethodHandles.lookup().findStatic(
+        Main.class, "collectCharSequence",
+        MethodType.methodType(int.class, String.class, CharSequence[].class));
+
+    // int[] is not convertible to CharSequence[].class.
+    try {
+      trailingRef.asCollector(int[].class, 1);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    // Object[] is not convertible to CharSequence[].class.
+    try {
+      trailingRef.asCollector(Object[].class, 1);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    // String[].class is convertible to CharSequence.class
+    MethodHandle collector = trailingRef.asCollector(String[].class, 2);
+    assertEquals(99, (int) collector.invoke("a", "b", "c"));
+
+    // Too few arguments should fail with a WMTE.
+    try {
+      collector.invoke("a", "b");
+      fail();
+    } catch (WrongMethodTypeException expected) {
+    }
+
+    // Too many arguments should fail with a WMTE.
+    try {
+      collector.invoke("a", "b", "c", "d");
+      fail();
+    } catch (WrongMethodTypeException expected) {
+    }
+
+    // Sanity checks on other array types.
+
+    MethodHandle target = MethodHandles.lookup().findStatic(
+        Main.class, "collectBoolean",
+        MethodType.methodType(int.class, String.class, boolean[].class));
+    assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectByte",
+        MethodType.methodType(int.class, String.class, byte[].class));
+    assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectChar",
+        MethodType.methodType(int.class, String.class, char[].class));
+    assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b'));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectShort",
+        MethodType.methodType(int.class, String.class, short[].class));
+    assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectInt",
+        MethodType.methodType(int.class, String.class, int[].class));
+    assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectLong",
+        MethodType.methodType(int.class, String.class, long[].class));
+    assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectFloat",
+        MethodType.methodType(int.class, String.class, float[].class));
+    assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f));
+
+    target = MethodHandles.lookup().findStatic(Main.class, "collectDouble",
+        MethodType.methodType(int.class, String.class, double[].class));
+    assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8));
+  }
+
   public static void fail() {
     System.out.println("FAIL");
     Thread.dumpStack();
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index cb798f0..b4c6b45 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -512,10 +512,13 @@
 # Known broken tests for the interpreter.
 # CFI unwinding expects managed frames.
 # 629 requires compilation.
+# 934 and 935 are broken due to the PreDefine hook not yet inserting them into the classpath. This should be fixed shortly
 TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
   137-cfi \
   554-jit-profile-file \
-  629-vdex-speed
+  629-vdex-speed \
+  934-load-transform \
+  935-non-retransformable \
 
 ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -538,6 +541,7 @@
 # resolved but until then just disable them. Test 916 already checks this
 # feature for JIT use cases in a way that is resilient to the jit frames.
 # 912: b/34655682
+# 934 and 935 are broken due to the PreDefine hook not yet inserting them into the classpath. This should be fixed shortly
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
   137-cfi \
   629-vdex-speed \
@@ -550,6 +554,8 @@
   917-fields-transformation \
   919-obsolete-fields \
   926-multi-obsolescence \
+  934-load-transform \
+  935-non-retransformable \
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 80e1797..330f7e1 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -329,6 +329,38 @@
 
 }  // namespace common_retransform
 
+namespace common_transform {
+
+using art::common_retransform::CommonClassFileLoadHookRetransformable;
+
+// Get all capabilities except those related to retransformation.
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  // Don't set the retransform caps
+  jvmtiCapabilities caps;
+  jvmti_env->GetPotentialCapabilities(&caps);
+  caps.can_retransform_classes = 0;
+  caps.can_retransform_any_class = 0;
+  jvmti_env->AddCapabilities(&caps);
+
+  // Use the same callback as the retransform test.
+  jvmtiEventCallbacks cb;
+  memset(&cb, 0, sizeof(cb));
+  cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
+  if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
+    printf("Unable to set class file load hook cb!\n");
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace common_transform
+
 static void BindMethod(jvmtiEnv* jenv,
                        JNIEnv* env,
                        jclass klass,
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index c60553d..0318501 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -27,10 +27,15 @@
 jint OnLoad(JavaVM* vm, char* options, void* reserved);
 
 }  // namespace common_redefine
+
 namespace common_retransform {
 jint OnLoad(JavaVM* vm, char* options, void* reserved);
 }  // namespace common_retransform
 
+namespace common_transform {
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+}  // namespace common_transform
+
 
 extern bool RuntimeIsJVM;
 
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 8ed8e67..f507451 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -110,6 +110,8 @@
   { "926-multi-obsolescence", common_redefine::OnLoad, nullptr },
   { "930-hello-retransform", common_retransform::OnLoad, nullptr },
   { "932-transform-saves", common_retransform::OnLoad, nullptr },
+  { "934-load-transform", common_retransform::OnLoad, nullptr },
+  { "935-non-retransformable", common_transform::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {