Merge "Revert^6 "Make class redefinition work with native methods on stack.""
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 4902ad4..59e6ac0 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -441,12 +441,56 @@
UNREACHABLE();
}
+// We use the method's DexFile and declaring class name to find the OatMethod for an obsolete
+// method. This is extremely slow but we need it if we want to be able to have obsolete native
+// methods since we need this to find the size of its stack frames.
+//
+// NB We could (potentially) do this differently and rely on the way the transformation is applied
+// in order to use the entrypoint to find this information. However, for debugging reasons (most
+// notably making sure that new invokes of obsolete methods fail) we choose to instead get the data
+// directly from the dex file.
+static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, bool* found)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(method->IsObsolete() && method->IsNative());
+ const DexFile* dex_file = method->GetDexFile();
+
+ // recreate the class_def_index from the descriptor.
+ std::string descriptor_storage;
+ const DexFile::TypeId* declaring_class_type_id =
+ dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage));
+ CHECK(declaring_class_type_id != nullptr);
+ dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id);
+ const DexFile::ClassDef* declaring_class_type_def =
+ dex_file->FindClassDef(declaring_class_type_index);
+ CHECK(declaring_class_type_def != nullptr);
+ uint16_t declaring_class_def_index = dex_file->GetIndexForClassDef(*declaring_class_type_def);
+
+ size_t oat_method_index = GetOatMethodIndexFromMethodIndex(*dex_file,
+ declaring_class_def_index,
+ method->GetDexMethodIndex());
+
+ OatFile::OatClass oat_class = OatFile::FindOatClass(*dex_file,
+ declaring_class_def_index,
+ found);
+ if (!(*found)) {
+ return OatFile::OatMethod::Invalid();
+ }
+ return oat_class.GetOatMethod(oat_method_index);
+}
+
static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method,
PointerSize pointer_size,
bool* found)
REQUIRES_SHARED(Locks::mutator_lock_) {
- // We shouldn't be calling this with obsolete methods.
- DCHECK(!method->IsObsolete());
+ if (UNLIKELY(method->IsObsolete())) {
+ // We shouldn't be calling this with obsolete methods except for native obsolete methods for
+ // which we need to use the oat method to figure out how large the quick frame is.
+ DCHECK(method->IsNative()) << "We should only be finding the OatMethod of obsolete methods in "
+ << "order to allow stack walking. Other obsolete methods should "
+ << "never need to access this information.";
+ DCHECK_EQ(pointer_size, kRuntimePointerSize) << "Obsolete method in compiler!";
+ return FindOatMethodFromDexFileFor(method, found);
+ }
// Although we overwrite the trampoline of non-static methods, we may get here via the resolution
// method for direct methods (or virtual methods made direct).
mirror::Class* declaring_class = method->GetDeclaringClass();
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3836303..3d51fdd 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -432,6 +432,7 @@
}
ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) {
+ DCHECK(!IsNative());
return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size));
}
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 0ac388a..60ab275 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -686,6 +686,10 @@
// shouldn't be used since it is no longer logically in the jit code cache.
// TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
+ // Native methods have no profiling info and need no special handling from the JIT code cache.
+ if (old_method->IsNative()) {
+ return;
+ }
MutexLock mu(Thread::Current(), lock_);
// Update ProfilingInfo to the new one and remove it from the old_method.
if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 843fd8c..d767c33 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -68,6 +68,66 @@
using android::base::StringPrintf;
+// A helper that fills in a classes obsolete_methods_ and obsolete_dex_caches_ classExt fields as
+// they are created. This ensures that we can always call any method of an obsolete ArtMethod object
+// almost as soon as they are created since the GetObsoleteDexCache method will succeed.
+class ObsoleteMap {
+ public:
+ art::ArtMethod* FindObsoleteVersion(art::ArtMethod* original)
+ REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
+ auto method_pair = id_map_.find(original);
+ if (method_pair != id_map_.end()) {
+ art::ArtMethod* res = obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
+ method_pair->second, art::kRuntimePointerSize);
+ DCHECK(res != nullptr);
+ DCHECK_EQ(original, res->GetNonObsoleteMethod());
+ return res;
+ } else {
+ return nullptr;
+ }
+ }
+
+ void RecordObsolete(art::ArtMethod* original, art::ArtMethod* obsolete)
+ REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
+ DCHECK(original != nullptr);
+ DCHECK(obsolete != nullptr);
+ int32_t slot = next_free_slot_++;
+ DCHECK_LT(slot, obsolete_methods_->GetLength());
+ DCHECK(nullptr ==
+ obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(slot, art::kRuntimePointerSize));
+ DCHECK(nullptr == obsolete_dex_caches_->Get(slot));
+ obsolete_methods_->SetElementPtrSize(slot, obsolete, art::kRuntimePointerSize);
+ obsolete_dex_caches_->Set(slot, original_dex_cache_);
+ id_map_.insert({original, slot});
+ }
+
+ ObsoleteMap(art::ObjPtr<art::mirror::PointerArray> obsolete_methods,
+ art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches,
+ art::ObjPtr<art::mirror::DexCache> original_dex_cache)
+ : next_free_slot_(0),
+ obsolete_methods_(obsolete_methods),
+ obsolete_dex_caches_(obsolete_dex_caches),
+ original_dex_cache_(original_dex_cache) {
+ // Figure out where the first unused slot in the obsolete_methods_ array is.
+ while (obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
+ next_free_slot_, art::kRuntimePointerSize) != nullptr) {
+ DCHECK(obsolete_dex_caches_->Get(next_free_slot_) != nullptr);
+ next_free_slot_++;
+ }
+ // Sanity check that the same slot in obsolete_dex_caches_ is free.
+ DCHECK(obsolete_dex_caches_->Get(next_free_slot_) == nullptr);
+ }
+
+ private:
+ int32_t next_free_slot_;
+ std::unordered_map<art::ArtMethod*, int32_t> id_map_;
+ // Pointers to the fields in mirror::ClassExt. These can be held as ObjPtr since this is only used
+ // when we have an exclusive mutator_lock_ (i.e. all threads are suspended).
+ art::ObjPtr<art::mirror::PointerArray> obsolete_methods_;
+ art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches_;
+ art::ObjPtr<art::mirror::DexCache> original_dex_cache_;
+};
+
// This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does
// some basic sanity checks that the obsolete method is sane.
class ObsoleteMethodStackVisitor : public art::StackVisitor {
@@ -76,7 +136,7 @@
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+ ObsoleteMap* obsolete_maps)
: StackVisitor(thread,
/*context*/nullptr,
StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -94,7 +154,7 @@
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+ ObsoleteMap* obsolete_maps)
REQUIRES(art::Locks::mutator_lock_) {
ObsoleteMethodStackVisitor visitor(thread,
allocator,
@@ -104,6 +164,7 @@
}
bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::ScopedAssertNoThreadSuspension snts("Fixing up the stack for obsolete methods.");
art::ArtMethod* old_method = GetMethod();
if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
// We cannot ensure that the right dex file is used in inlined frames so we don't support
@@ -113,9 +174,8 @@
// TODO We should really support redefining intrinsics.
// We don't support intrinsics so check for them here.
DCHECK(!old_method->IsIntrinsic());
- art::ArtMethod* new_obsolete_method = nullptr;
- auto obsolete_method_pair = obsolete_maps_->find(old_method);
- if (obsolete_method_pair == obsolete_maps_->end()) {
+ art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
+ if (new_obsolete_method == nullptr) {
// Create a new Obsolete Method and put it in the list.
art::Runtime* runtime = art::Runtime::Current();
art::ClassLinker* cl = runtime->GetClassLinker();
@@ -129,7 +189,7 @@
DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
new_obsolete_method->SetIsObsolete();
new_obsolete_method->SetDontCompile();
- obsolete_maps_->insert({old_method, new_obsolete_method});
+ obsolete_maps_->RecordObsolete(old_method, new_obsolete_method);
// Update JIT Data structures to point to the new method.
art::jit::Jit* jit = art::Runtime::Current()->GetJit();
if (jit != nullptr) {
@@ -137,8 +197,6 @@
// structures to keep track of the new obsolete method.
jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method);
}
- } else {
- new_obsolete_method = obsolete_method_pair->second;
}
DCHECK(new_obsolete_method != nullptr);
SetMethod(new_obsolete_method);
@@ -152,9 +210,9 @@
// The set of all methods which could be obsoleted.
const std::unordered_set<art::ArtMethod*>& obsoleted_methods_;
// A map from the original to the newly allocated obsolete method for frames on this thread. The
- // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
- // the redefined classes ClassExt by the caller.
- std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
+ // values in this map are added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
+ // the redefined classes ClassExt as it is filled.
+ ObsoleteMap* obsolete_maps_;
};
jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
@@ -431,11 +489,12 @@
}
struct CallbackCtx {
+ ObsoleteMap* obsolete_map;
art::LinearAlloc* allocator;
- std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
std::unordered_set<art::ArtMethod*> obsolete_methods;
- explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {}
+ explicit CallbackCtx(ObsoleteMap* map, art::LinearAlloc* alloc)
+ : obsolete_map(map), allocator(alloc) {}
};
void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
@@ -443,7 +502,7 @@
ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
data->allocator,
data->obsolete_methods,
- &data->obsolete_map);
+ data->obsolete_map);
}
// This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
@@ -454,9 +513,18 @@
art::mirror::ClassExt* ext = art_klass->GetExtData();
CHECK(ext->GetObsoleteMethods() != nullptr);
art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
- CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
+ // This holds pointers to the obsolete methods map fields which are updated as needed.
+ ObsoleteMap map(ext->GetObsoleteMethods(), ext->GetObsoleteDexCaches(), art_klass->GetDexCache());
+ CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
// Add all the declared methods to the map
for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ // It is possible to simply filter out some methods where they cannot really become obsolete,
+ // such as native methods and keep their original (possibly optimized) implementations. We don't
+ // do this, however, since we would need to mark these functions (still in the classes
+ // declared_methods array) as obsolete so we will find the correct dex file to get meta-data
+ // from (for example about stack-frame size). Furthermore we would be unable to get some useful
+ // error checking from the interpreter which ensure we don't try to start executing obsolete
+ // methods.
ctx.obsolete_methods.insert(&m);
// TODO Allow this or check in IsModifiableClass.
DCHECK(!m.IsIntrinsic());
@@ -466,36 +534,6 @@
art::ThreadList* list = art::Runtime::Current()->GetThreadList();
list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
}
- FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
-}
-
-// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
-// figure out their DexCaches.
-void Redefiner::ClassRedefinition::FillObsoleteMethodMap(
- art::mirror::Class* art_klass,
- const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) {
- int32_t index = 0;
- art::mirror::ClassExt* ext_data = art_klass->GetExtData();
- art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods();
- art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches =
- ext_data->GetObsoleteDexCaches();
- int32_t num_method_slots = obsolete_methods->GetLength();
- // Find the first empty index.
- for (; index < num_method_slots; index++) {
- if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>(
- index, art::kRuntimePointerSize) == nullptr) {
- break;
- }
- }
- // Make sure we have enough space.
- CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index));
- CHECK(obsolete_dex_caches->Get(index) == nullptr);
- // Fill in the map.
- for (auto& obs : obsoletes) {
- obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize);
- obsolete_dex_caches->Set(index, art_klass->GetDexCache());
- index++;
- }
}
// Try and get the declared method. First try to get a virtual method then a direct method if that's
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index c441377..65ee291 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -155,12 +155,6 @@
void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
REQUIRES(art::Locks::mutator_lock_);
- void FillObsoleteMethodMap(
- art::mirror::Class* art_klass,
- const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
- REQUIRES(art::Locks::mutator_lock_);
-
-
// Checks that the dex file contains only the single expected class and that the top-level class
// data has not been modified in an incompatible manner.
bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/stack.cc b/runtime/stack.cc
index d7ba1d7..51a24e4 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -874,9 +874,13 @@
CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee)
<< " Found: " << ArtMethod::PrettyMethod(GetMethod());
} else {
- CHECK_EQ(instrumentation_frame.method_, GetMethod())
- << "Expected: " << ArtMethod::PrettyMethod(instrumentation_frame.method_)
- << " Found: " << ArtMethod::PrettyMethod(GetMethod());
+ // Instrumentation generally doesn't distinguish between a method's obsolete and
+ // non-obsolete version.
+ CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(),
+ GetMethod()->GetNonObsoleteMethod())
+ << "Expected: "
+ << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod())
+ << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod());
}
if (num_frames_ != 0) {
// Check agreement of frame Ids only if num_frames_ is computed to avoid infinite
@@ -903,7 +907,7 @@
<< " native=" << method->IsNative()
<< std::noboolalpha
<< " entrypoints=" << method->GetEntryPointFromQuickCompiledCode()
- << "," << method->GetEntryPointFromJni()
+ << "," << (method->IsNative() ? method->GetEntryPointFromJni() : nullptr)
<< " next=" << *cur_quick_frame_;
}
diff --git a/test/945-obsolete-native/build b/test/945-obsolete-native/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/945-obsolete-native/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/945-obsolete-native/expected.txt b/test/945-obsolete-native/expected.txt
new file mode 100644
index 0000000..83efda1
--- /dev/null
+++ b/test/945-obsolete-native/expected.txt
@@ -0,0 +1,9 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+goodbye
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/945-obsolete-native/info.txt b/test/945-obsolete-native/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/945-obsolete-native/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
new file mode 100644
index 0000000..061e7af
--- /dev/null
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/stringprintf.h"
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test945ObsoleteNative {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ BindFunctions(jvmti_env, env, "Transform");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject runnable) {
+ jclass runnable_klass = env->FindClass("java/lang/Runnable");
+ DCHECK(runnable_klass != nullptr);
+ jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V");
+ env->CallVoidMethod(runnable, run_method);
+}
+
+
+} // namespace Test945ObsoleteNative
+} // namespace art
diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/945-obsolete-native/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
diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java
new file mode 100644
index 0000000..5e2154e
--- /dev/null
+++ b/test/945-obsolete-native/src/Main.java
@@ -0,0 +1,77 @@
+/*
+ * 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 {
+ // class Transform {
+ // public void sayHi(Runnable r) {
+ // System.out.println("Hello - Transformed");
+ // doExecute(r);
+ // System.out.println("Goodbye - Transformed");
+ // }
+ //
+ // private static native void doExecute(Runnable r);
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+ "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" +
+ "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" +
+ "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
+ "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" +
+ "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" +
+ "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" +
+ "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" +
+ "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" +
+ "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" +
+ "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+ "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" +
+ "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" +
+ "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" +
+ "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+ "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+ "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+ "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" +
+ "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" +
+ "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" +
+ "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" +
+ "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" +
+ "AA==");
+
+ public static void main(String[] args) {
+ bindTest945ObsoleteNative();
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ System.out.println("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ private static native void bindTest945ObsoleteNative();
+}
diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java
new file mode 100644
index 0000000..2b7cc1b
--- /dev/null
+++ b/test/945-obsolete-native/src/Transform.java
@@ -0,0 +1,25 @@
+/*
+ * 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(Runnable r) {
+ System.out.println("hello");
+ doExecute(r);
+ System.out.println("goodbye");
+ }
+
+ private static native void doExecute(Runnable r);
+}
diff --git a/test/Android.bp b/test/Android.bp
index d3244a6..00c890a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -274,6 +274,7 @@
"933-misc-events/misc_events.cc",
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
+ "945-obsolete-native/obsolete_native.cc",
],
shared_libs: [
"libbase",
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index c5a9356..351857d 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -122,6 +122,7 @@
{ "942-private-recursive", common_redefine::OnLoad, nullptr },
{ "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
{ "944-transform-classloaders", common_redefine::OnLoad, nullptr },
+ { "945-obsolete-native", common_redefine::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {