diff options
author | 2017-02-16 15:38:35 -0800 | |
---|---|---|
committer | 2017-02-17 08:33:06 -0800 | |
commit | ea9465eaaa55646b0de242d2a21f9c1f0f0aa01f (patch) | |
tree | 12590422d05d459a63a56937e77520e84cf4c288 | |
parent | 4c9c57054578022d9ab8442264fbc661769f97f5 (diff) |
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
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 102 | ||||
-rw-r--r-- | runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc | 198 | ||||
-rw-r--r-- | runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h | 28 | ||||
-rw-r--r-- | runtime/runtime.cc | 2 | ||||
-rw-r--r-- | test/949-in-memory-transform/expected.txt | 2 | ||||
-rw-r--r-- | test/949-in-memory-transform/info.txt | 4 | ||||
-rwxr-xr-x | test/949-in-memory-transform/run | 17 | ||||
-rw-r--r-- | test/949-in-memory-transform/src/Main.java | 124 |
9 files changed, 249 insertions, 229 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index d3a81a9add..d136aa15b3 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -143,7 +143,6 @@ cc_defaults { "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 1234933db9..0e61cf64f9 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -155,6 +155,105 @@ class NullableScopedUtfChars { 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 @@ static JNINativeMethod gMethods[] = { "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 07959607fc..0000000000 --- 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 f73d07a618..0000000000 --- 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 f8f3d766c0..69dcfebcb1 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 @@ jobject Runtime::GetSystemClassLoader() const { 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 0000000000..4774b81b49 --- /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 0000000000..7753729dc7 --- /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 0000000000..e92b873956 --- /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 0000000000..2ffabf5424 --- /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); +} |