Move InMemoryDexClassLoader to be a subtype of BaseDexClassLoader
Also write a test that class transformation works with this type of
class loader.
Test: mma -j40 test-art-host
Change-Id: Ibc2214191e04876ff7bbea010be6ec03b6f41904
diff --git a/runtime/Android.bp b/runtime/Android.bp
index d3a81a9..d136aa1 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -143,7 +143,6 @@
"native_bridge_art_interface.cc",
"native_stack_dump.cc",
"native/dalvik_system_DexFile.cc",
- "native/dalvik_system_InMemoryDexClassLoader_DexData.cc",
"native/dalvik_system_VMDebug.cc",
"native/dalvik_system_VMRuntime.cc",
"native/dalvik_system_VMStack.cc",
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 1234933..0e61cf6 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -155,6 +155,105 @@
void operator=(const NullableScopedUtfChars&);
};
+static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
+ if (end <= start) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("Bad range");
+ return nullptr;
+ }
+
+ std::string error_message;
+ size_t length = static_cast<size_t>(end - start);
+ std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
+ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ &error_message));
+ if (dex_mem_map == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("%s", error_message.c_str());
+ }
+ return dex_mem_map;
+}
+
+static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
+ std::string location = StringPrintf("Anonymous-DexFile@%p-%p",
+ dex_mem_map->Begin(),
+ dex_mem_map->End());
+ std::string error_message;
+ std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
+ 0,
+ std::move(dex_mem_map),
+ /* verify */ true,
+ /* verify_location */ true,
+ &error_message));
+ if (dex_file == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("%s", error_message.c_str());
+ return nullptr;
+ }
+
+ if (!dex_file->DisableWrite()) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("Failed to make dex file read-only");
+ return nullptr;
+ }
+
+ return dex_file.release();
+}
+
+static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data) {
+ std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(data)));
+ if (dex_file.get() == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files.push_back(std::move(dex_file));
+ return ConvertDexFilesToJavaArray(env, nullptr, dex_files);
+}
+
+static jobject DexFile_createCookieWithDirectBuffer(JNIEnv* env,
+ jclass,
+ jobject buffer,
+ jint start,
+ jint end) {
+ uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
+ if (base_address == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("dexFileBuffer not direct");
+ return 0;
+ }
+
+ std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+ if (dex_mem_map == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return 0;
+ }
+
+ size_t length = static_cast<size_t>(end - start);
+ memcpy(dex_mem_map->Begin(), base_address, length);
+ return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
+}
+
+static jobject DexFile_createCookieWithArray(JNIEnv* env,
+ jclass,
+ jbyteArray buffer,
+ jint start,
+ jint end) {
+ std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+ if (dex_mem_map == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return 0;
+ }
+
+ auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
+ env->GetByteArrayRegion(buffer, start, end - start, destination);
+ return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
+}
+
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
@@ -591,6 +690,9 @@
"Ljava/lang/ClassLoader;"
"[Ldalvik/system/DexPathList$Element;"
")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile, createCookieWithDirectBuffer,
+ "(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile, createCookieWithArray, "([BII)Ljava/lang/Object;"),
NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile,
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
deleted file mode 100644
index 0795960..0000000
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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.
- */
-
-#include "dalvik_system_InMemoryDexClassLoader_DexData.h"
-
-#include "android-base/stringprintf.h"
-
-#include "class_linker.h"
-#include "common_throws.h"
-#include "dex_file.h"
-#include "jni_internal.h"
-#include "mem_map.h"
-#include "mirror/class_loader.h"
-#include "mirror/object-inl.h"
-#include "oat_file.h"
-#include "scoped_thread_state_change-inl.h"
-#include "ScopedUtfChars.h"
-
-namespace art {
-
-using android::base::StringPrintf;
-
-static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
- if (end <= start) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("Bad range");
- return nullptr;
- }
-
- std::string error_message;
- size_t length = static_cast<size_t>(end - start);
- std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
- nullptr,
- length,
- PROT_READ | PROT_WRITE,
- /* low_4gb */ false,
- /* reuse */ false,
- &error_message));
- if (dex_mem_map == nullptr) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("%s", error_message.c_str());
- }
- return dex_mem_map;
-}
-
-static jlong DexFileToCookie(const DexFile* dex_file) {
- return reinterpret_cast<jlong>(dex_file);
-}
-
-static const DexFile* CookieToDexFile(jlong cookie) {
- return reinterpret_cast<const DexFile*>(cookie);
-}
-
-static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
- std::string location = StringPrintf("InMemoryDexClassLoader_DexData@%p-%p",
- dex_mem_map->Begin(),
- dex_mem_map->End());
- std::string error_message;
- std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
- 0,
- std::move(dex_mem_map),
- /* verify */ true,
- /* verify_location */ true,
- &error_message));
- if (dex_file == nullptr) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("%s", error_message.c_str());
- return nullptr;
- }
-
- if (!dex_file->DisableWrite()) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("Failed to make dex file read-only");
- return nullptr;
- }
-
- return dex_file.release();
-}
-
-static jlong InMemoryDexClassLoader_DexData_initializeWithDirectBuffer(
- JNIEnv* env, jclass, jobject buffer, jint start, jint end) {
- uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
- if (base_address == nullptr) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("dexFileBuffer not direct");
- return 0;
- }
-
- std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
- if (dex_mem_map == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return 0;
- }
-
- size_t length = static_cast<size_t>(end - start);
- memcpy(dex_mem_map->Begin(), base_address, length);
- return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
-}
-
-static jlong InMemoryDexClassLoader_DexData_initializeWithArray(
- JNIEnv* env, jclass, jbyteArray buffer, jint start, jint end) {
- std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
- if (dex_mem_map == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return 0;
- }
-
- auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
- env->GetByteArrayRegion(buffer, start, end - start, destination);
- return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
-}
-
-static void InMemoryDexClassLoader_DexData_uninitialize(JNIEnv* env, jclass, jlong cookie) {
- const DexFile* dex_file = CookieToDexFile(cookie);
- if (kIsDebugBuild) {
- ScopedObjectAccess soa(env);
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- CHECK(!class_linker->IsDexFileRegistered(soa.Self(), *dex_file));
- }
- delete dex_file;
-}
-
-static jclass InMemoryDexClassLoader_DexData_findClass(
- JNIEnv* env, jobject dexData, jstring name, jobject loader, jlong cookie) {
- ScopedUtfChars scoped_class_name(env, name);
- if (env->ExceptionCheck()) {
- return nullptr;
- }
-
- const char* class_name = scoped_class_name.c_str();
- const std::string descriptor(DotToDescriptor(class_name));
- const char* class_descriptor = descriptor.c_str();
- const size_t hash = ComputeModifiedUtf8Hash(class_descriptor);
- const DexFile* dex_file = CookieToDexFile(cookie);
- const DexFile::ClassDef* dex_class_def =
- OatDexFile::FindClassDef(*dex_file, class_descriptor, hash);
- if (dex_class_def != nullptr) {
- ScopedObjectAccess soa(env);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> handle_scope(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- handle_scope.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));
- ObjPtr<mirror::DexCache> dex_cache =
- class_linker->RegisterDexFile(*dex_file, class_loader.Get());
- if (dex_cache == nullptr) {
- // OOME or InternalError (dexFile already registered with a different class loader).
- soa.Self()->AssertPendingException();
- return nullptr;
- }
- ObjPtr<mirror::Class> result = class_linker->DefineClass(
- soa.Self(),
- class_descriptor,
- hash, class_loader,
- *dex_file,
- *dex_class_def);
- if (result != nullptr) {
- // Ensure the class table has a strong reference to the
- // InMemoryClassLoader/DexData instance now that a class has
- // been loaded.
- class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexData),
- class_loader.Get());
- return soa.AddLocalReference<jclass>(result);
- }
- }
-
- VLOG(class_linker) << "Failed to find dex_class_def " << class_name;
- return nullptr;
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(InMemoryDexClassLoader_DexData,
- initializeWithDirectBuffer,
- "(Ljava/nio/ByteBuffer;II)J"),
- NATIVE_METHOD(InMemoryDexClassLoader_DexData, initializeWithArray, "([BII)J"),
- NATIVE_METHOD(InMemoryDexClassLoader_DexData, uninitialize, "(J)V"),
- NATIVE_METHOD(InMemoryDexClassLoader_DexData,
- findClass,
- "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
-};
-
-void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env) {
- REGISTER_NATIVE_METHODS("dalvik/system/InMemoryDexClassLoader$DexData");
-}
-
-} // namespace art
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
deleted file mode 100644
index f73d07a..0000000
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
-#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
-
-#include <jni.h>
-
-namespace art {
-
-void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env);
-
-} // namespace art
-
-#endif // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f8f3d76..69dcfeb 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -101,7 +101,6 @@
#include "mirror/throwable.h"
#include "monitor.h"
#include "native/dalvik_system_DexFile.h"
-#include "native/dalvik_system_InMemoryDexClassLoader_DexData.h"
#include "native/dalvik_system_VMDebug.h"
#include "native/dalvik_system_VMRuntime.h"
#include "native/dalvik_system_VMStack.h"
@@ -1534,7 +1533,6 @@
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_dalvik_system_DexFile(env);
- register_dalvik_system_InMemoryDexClassLoader_DexData(env);
register_dalvik_system_VMDebug(env);
register_dalvik_system_VMRuntime(env);
register_dalvik_system_VMStack(env);
diff --git a/test/949-in-memory-transform/expected.txt b/test/949-in-memory-transform/expected.txt
new file mode 100644
index 0000000..4774b81
--- /dev/null
+++ b/test/949-in-memory-transform/expected.txt
@@ -0,0 +1,2 @@
+hello
+Goodbye
diff --git a/test/949-in-memory-transform/info.txt b/test/949-in-memory-transform/info.txt
new file mode 100644
index 0000000..7753729
--- /dev/null
+++ b/test/949-in-memory-transform/info.txt
@@ -0,0 +1,4 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that transformation works even when the class being transformed comes from
+an in-memory dex file (i.e. loaded from an InMemoryDexClassLoader).
diff --git a/test/949-in-memory-transform/run b/test/949-in-memory-transform/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/949-in-memory-transform/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 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
diff --git a/test/949-in-memory-transform/src/Main.java b/test/949-in-memory-transform/src/Main.java
new file mode 100644
index 0000000..2ffabf5
--- /dev/null
+++ b/test/949-in-memory-transform/src/Main.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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;
+import java.lang.reflect.*;
+import java.nio.ByteBuffer;
+
+public class Main {
+ /**
+ * base64 encoded class/dex file for
+ * public class Transform {
+ * public void sayHi() {
+ * System.out.println("hello");
+ * }
+ * }
+ */
+ private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" +
+ "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAA" +
+ "AAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" +
+ "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN");
+ private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAJX3mZphwHJCT1qdTz/GS+jXOR+O/9e3fMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" +
+ "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" +
+ "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+ "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+ "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAANvdXQA" +
+ "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCBgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA=");
+
+
+ /**
+ * base64 encoded class/dex file for
+ * public class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] TRANSFORMED_CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+ "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+ "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAG" +
+ "AAAAAAACAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+ "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAaAAgAGwABAAwAAAACAA0=");
+ private static final byte[] TRANSFORMED_DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAPXh6T3l1FObhHsKf1U2vi+0GmAvElxBLMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgAaAAcOhwAAAAEBAIGABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+ public static void main(String[] args) throws Exception {
+ ClassLoader loader;
+ try {
+ // Art uses this classloader to do in-memory dex files. There is no support for defineClass
+ loader = (ClassLoader)Class.forName("dalvik.system.InMemoryDexClassLoader")
+ .getConstructor(ByteBuffer.class, ClassLoader.class)
+ .newInstance(ByteBuffer.wrap(INITIAL_DEX_BYTES),
+ ClassLoader.getSystemClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Seem to be on RI. Just make a new ClassLoader that calls defineClass.
+ loader = new ClassLoader() {
+ public Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("Transform")) {
+ return defineClass(name, INITIAL_CLASS_BYTES, 0, INITIAL_CLASS_BYTES.length);
+ } else {
+ throw new ClassNotFoundException("Couldn't find class: " + name);
+ }
+ }
+ };
+ }
+ doTest(loader);
+ }
+
+ public static void doTest(ClassLoader loader) throws Exception {
+ // Get the class
+ Class<?> transform_class = loader.loadClass("Transform");
+ Method say_hi_method = transform_class.getMethod("sayHi");
+ Object t = transform_class.newInstance();
+
+ // Run the actual test.
+ say_hi_method.invoke(t);
+ doCommonClassRedefinition(transform_class, TRANSFORMED_CLASS_BYTES, TRANSFORMED_DEX_BYTES);
+ say_hi_method.invoke(t);
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}