Merge "Implemented ABS vectorization."
diff --git a/runtime/arch/arm/instruction_set_features_arm_test.cc b/runtime/arch/arm/instruction_set_features_arm_test.cc
index 6d5dd6d..3582351 100644
--- a/runtime/arch/arm/instruction_set_features_arm_test.cc
+++ b/runtime/arch/arm/instruction_set_features_arm_test.cc
@@ -34,6 +34,18 @@
EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", krait_features->GetFeatureString().c_str());
EXPECT_EQ(krait_features->AsBitmap(), 3U);
+ // Build features for a 32-bit ARM kryo processor.
+ std::unique_ptr<const InstructionSetFeatures> kryo_features(
+ InstructionSetFeatures::FromVariant(kArm, "kryo", &error_msg));
+ ASSERT_TRUE(kryo_features.get() != nullptr) << error_msg;
+
+ ASSERT_EQ(kryo_features->GetInstructionSet(), kArm);
+ EXPECT_TRUE(kryo_features->Equals(kryo_features.get()));
+ EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+ EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
+ EXPECT_STREQ("div,atomic_ldrd_strd,armv8a", kryo_features->GetFeatureString().c_str());
+ EXPECT_EQ(kryo_features->AsBitmap(), 7U);
+
// Build features for a 32-bit ARM denver processor.
std::unique_ptr<const InstructionSetFeatures> denver_features(
InstructionSetFeatures::FromVariant(kArm, "denver", &error_msg));
@@ -86,6 +98,18 @@
EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", krait_features->GetFeatureString().c_str());
EXPECT_EQ(krait_features->AsBitmap(), 3U);
+ // Build features for a 32-bit ARM with LPAE and div processor.
+ std::unique_ptr<const InstructionSetFeatures> kryo_features(
+ base_features->AddFeaturesFromString("atomic_ldrd_strd,div", &error_msg));
+ ASSERT_TRUE(kryo_features.get() != nullptr) << error_msg;
+
+ ASSERT_EQ(kryo_features->GetInstructionSet(), kArm);
+ EXPECT_TRUE(kryo_features->Equals(krait_features.get()));
+ EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+ EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
+ EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", kryo_features->GetFeatureString().c_str());
+ EXPECT_EQ(kryo_features->AsBitmap(), 3U);
+
// Build features for a 32-bit ARM processor with LPAE and div flipped.
std::unique_ptr<const InstructionSetFeatures> denver_features(
base_features->AddFeaturesFromString("div,atomic_ldrd_strd,armv8a", &error_msg));
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 72aa785..029de46 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1614,6 +1614,11 @@
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
+/*
+ * Called to attempt to execute an obsolete method.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
/*
* Routine that intercepts method calls and returns.
*/
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 26622f0..b2bbd0d 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2152,6 +2152,11 @@
RETURN_OR_DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
+/*
+ * Called to attempt to execute an obsolete method.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
//
// Instrumentation-related stubs
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 808536b..722a679 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1877,6 +1877,14 @@
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
+ .extern artInvokeObsoleteMethod
+ENTRY art_invoke_obsolete_method_stub
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ la $t9, artInvokeObsoleteMethod
+ jalr $t9 # (Method* method, Thread* self)
+ move $a1, rSELF # pass Thread::Current
+END art_invoke_obsolete_method_stub
+
/*
* Routine that intercepts method calls and returns.
*/
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 9c92805..9402232 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1818,6 +1818,13 @@
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
+ .extern artInvokeObsoleteMethod
+ENTRY art_invoke_obsolete_method_stub
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ jal artInvokeObsoleteMethod # (Method* method, Thread* self)
+ move $a1, rSELF # pass Thread::Current
+END art_invoke_obsolete_method_stub
+
/*
* Routine that intercepts method calls and returns.
*/
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 5f38dc8..6c0bcc9 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1938,6 +1938,11 @@
END_FUNCTION art_quick_to_interpreter_bridge
/*
+ * Called by managed code, saves callee saves and then calls artInvokeObsoleteMethod
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
+ /*
* Routine that intercepts method calls and returns.
*/
DEFINE_FUNCTION art_quick_instrumentation_entry
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index e87b165..8e2acab 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1902,6 +1902,12 @@
END_FUNCTION art_quick_to_interpreter_bridge
/*
+ * Called to catch an attempt to invoke an obsolete method.
+ * RDI = method being called.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
+ /*
* Routine that intercepts method calls and returns.
*/
DEFINE_FUNCTION art_quick_instrumentation_entry
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 80a8773..5a71be6 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -337,7 +337,8 @@
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
CHECK(!runtime->UseJitCompilation());
- const void* oat_quick_code = (IsNative() || !IsInvokable() || IsProxyMethod())
+ const void* oat_quick_code =
+ (IsNative() || !IsInvokable() || IsProxyMethod() || IsObsolete())
? nullptr
: GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize());
CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3c18704..b8ff2c2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8507,6 +8507,15 @@
}
}
+void ClassLinker::SetEntryPointsForObsoleteMethod(ArtMethod* method) const {
+ DCHECK(method->IsObsolete());
+ // We cannot mess with the entrypoints of native methods because they are used to determine how
+ // large the method's quick stack frame is. Without this information we cannot walk the stacks.
+ if (!method->IsNative()) {
+ method->SetEntryPointFromQuickCompiledCode(GetInvokeObsoleteMethodStub());
+ }
+}
+
void ClassLinker::DumpForSigQuit(std::ostream& os) {
ScopedObjectAccess soa(Thread::Current());
ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index ef51d82..a26e63b 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -544,6 +544,10 @@
void SetEntryPointsToInterpreter(ArtMethod* method) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Set the entrypoints up for an obsolete method.
+ void SetEntryPointsForObsoleteMethod(ArtMethod* method) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Attempts to insert a class into a class table. Returns null if
// the class was inserted, otherwise returns an existing class with
// the same descriptor and ClassLoader.
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 4f4bed0..6758d75 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -313,6 +313,14 @@
ArtMethod::PrettyMethod(method).c_str()).c_str());
}
+// InternalError
+
+void ThrowInternalError(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException("Ljava/lang/InternalError;", nullptr, fmt, &args);
+ va_end(args);
+}
// IOException
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 55a8938..4afef79 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -151,6 +151,12 @@
void ThrowIncompatibleClassChangeErrorForMethodConflict(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// InternalError
+
+void ThrowInternalError(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 1, 2)))
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// IOException
void ThrowIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)))
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 1520e13..565b4ed 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -29,6 +29,15 @@
self->QuickDeliverException();
}
+extern "C" NO_RETURN uint64_t artInvokeObsoleteMethod(ArtMethod* method, Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(method->IsObsolete());
+ ScopedQuickEntrypointChecks sqec(self);
+ ThrowInternalError("Attempting to invoke obsolete version of '%s'.",
+ method->PrettyMethod().c_str());
+ self->QuickDeliverException();
+}
+
// Called by generated code to throw an exception.
extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index 2842c5a..4ca52de 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -40,6 +40,12 @@
return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge);
}
+// Return the address of stub code for attempting to invoke an obsolete method.
+extern "C" void art_invoke_obsolete_method_stub(ArtMethod*);
+static inline const void* GetInvokeObsoleteMethodStub() {
+ return reinterpret_cast<const void*>(art_invoke_obsolete_method_stub);
+}
+
// Return the address of quick stub code for handling JNI calls.
extern "C" void art_quick_generic_jni_trampoline(ArtMethod*);
static inline const void* GetQuickGenericJniStub() {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 67e949f..bf49e84 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -360,6 +360,14 @@
return;
}
+ // This can happen if we are in forced interpreter mode and an obsolete method is called using
+ // reflection.
+ if (UNLIKELY(method->IsObsolete())) {
+ ThrowInternalError("Attempting to invoke obsolete version of '%s'.",
+ method->PrettyMethod().c_str());
+ return;
+ }
+
const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
const DexFile::CodeItem* code_item = method->GetCodeItem();
uint16_t num_regs;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 6ca951f..11f8505 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -451,8 +451,12 @@
Thread* const self = Thread::Current();
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
CHECK(dex_file != nullptr);
+ // In fallback mode, not all boot classpath components might be registered, yet.
+ if (!class_linker->IsDexFileRegistered(self, *dex_file)) {
+ continue;
+ }
ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file);
- CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded.
+ DCHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded.
for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(j));
if (string != nullptr) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 6e0d9a0..7d95de8 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -186,6 +186,7 @@
DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
new_obsolete_method->SetIsObsolete();
new_obsolete_method->SetDontCompile();
+ cl->SetEntryPointsForObsoleteMethod(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();
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 0628643..333128b 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -649,7 +649,7 @@
}
const void* code = method->GetEntryPointFromQuickCompiledCode();
- if (code == GetQuickInstrumentationEntryPoint()) {
+ if (code == GetQuickInstrumentationEntryPoint() || code == GetInvokeObsoleteMethodStub()) {
return;
}
diff --git a/sigchainlib/OWNERS b/sigchainlib/OWNERS
new file mode 100644
index 0000000..450fc12
--- /dev/null
+++ b/sigchainlib/OWNERS
@@ -0,0 +1,4 @@
+# Default maintainers and code reviewers:
+jmgao@google.com
+dimitry@google.com
+sehr@google.com
diff --git a/test/984-obsolete-invoke/expected.txt b/test/984-obsolete-invoke/expected.txt
new file mode 100644
index 0000000..8052c46
--- /dev/null
+++ b/test/984-obsolete-invoke/expected.txt
@@ -0,0 +1,10 @@
+hello
+transforming calling function
+Retrieving obsolete method from current stack
+goodbye
+Invoking redefined version of method.
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
+invoking obsolete method
+Caught expected error from attempting to invoke an obsolete method.
diff --git a/test/984-obsolete-invoke/info.txt b/test/984-obsolete-invoke/info.txt
new file mode 100644
index 0000000..48e0de0
--- /dev/null
+++ b/test/984-obsolete-invoke/info.txt
@@ -0,0 +1,4 @@
+Tests basic obsolete method support
+
+Tests that a specific method of potentially executing obsolete method code does
+not work.
diff --git a/test/984-obsolete-invoke/obsolete_invoke.cc b/test/984-obsolete-invoke/obsolete_invoke.cc
new file mode 100644
index 0000000..27e36ba
--- /dev/null
+++ b/test/984-obsolete-invoke/obsolete_invoke.cc
@@ -0,0 +1,70 @@
+/*
+ * 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 "android-base/macros.h"
+#include "jni.h"
+#include "jvmti.h"
+#include "mirror/class-inl.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "test_env.h"
+
+#include "jvmti_helper.h"
+
+namespace art {
+namespace Test984ObsoleteInvoke {
+
+static constexpr size_t kNumFrames = 30;
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getFirstObsoleteMethod984(JNIEnv* env, jclass) {
+ jthread cur;
+ jint frame_count;
+ jvmtiFrameInfo frames[kNumFrames];
+ // jint cur_start = 0;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetCurrentThread(&cur))) {
+ // ERROR
+ return nullptr;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetStackTrace(cur,
+ 0,
+ kNumFrames,
+ frames,
+ &frame_count))) {
+ // ERROR
+ return nullptr;
+ }
+ for (jint i = 0; i < frame_count; i++) {
+ jmethodID method = frames[i].method;
+ jboolean is_obsolete = false;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->IsMethodObsolete(method, &is_obsolete))) {
+ // ERROR
+ return nullptr;
+ }
+ if (is_obsolete) {
+ return env->ToReflectedMethod(env->FindClass("java/lang/reflect/Method"),
+ method,
+ JNI_TRUE);
+ }
+ }
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find obsolete method!");
+ return nullptr;
+}
+
+} // namespace Test984ObsoleteInvoke
+} // namespace art
diff --git a/test/984-obsolete-invoke/run b/test/984-obsolete-invoke/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/984-obsolete-invoke/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/984-obsolete-invoke/src/Main.java b/test/984-obsolete-invoke/src/Main.java
new file mode 100644
index 0000000..1a8d9bc
--- /dev/null
+++ b/test/984-obsolete-invoke/src/Main.java
@@ -0,0 +1,108 @@
+/*
+ * 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 {
+ // class Transform {
+ // public static void sayHi(Runnable r) {
+ // System.out.println("Hello - Transformed");
+ // r.run();
+ // System.out.println("Goodbye - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+ "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
+ "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
+ "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+ "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
+ "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQAJAA0ADgABAAsAAAA7AAIA" +
+ "AQAAABeyAAISA7YABCq5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
+ "AQAPAAAAAgAQ");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCMekj2NPwzrEp/v+2yzzSg8xZvBtU1bC1QAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
+ "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
+ "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
+ "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+ "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
+ "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
+ "AwAAAA4AAwABAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAgBiAAAAGwEBAAAAbiAC" +
+ "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+ "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+ "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+ "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
+ "LjMxAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAIAAICABMQCAQnc" +
+ "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
+ "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
+ "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
+
+ public static void main(String[] args) {
+ doTest();
+ }
+
+ // The Method that holds an obsolete method pointer. We will fill it in by getting a jmethodID
+ // from a stack with an obsolete method in it. There should be no other ways to obtain an obsolete
+ // jmethodID in ART without unsafe casts.
+ public static Method obsolete_method = null;
+
+ public static void doTest() {
+ // Capture the obsolete method.
+ //
+ // NB The obsolete method must be direct so that we will not look in the receiver type to get
+ // the actual method.
+ Transform.sayHi(() -> {
+ System.out.println("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("Retrieving obsolete method from current stack");
+ // This should get the obsolete sayHi method (as the only obsolete method on the current
+ // threads stack).
+ Main.obsolete_method = getFirstObsoleteMethod984();
+ });
+
+ // Prove we did actually redefine something.
+ System.out.println("Invoking redefined version of method.");
+ Transform.sayHi(() -> { System.out.println("Not doing anything here"); });
+
+ System.out.println("invoking obsolete method");
+ try {
+ obsolete_method.invoke(null, (Runnable)() -> {
+ throw new Error("Unexpected code running from invoke of obsolete method!");
+ });
+ throw new Error("Running obsolete method did not throw exception");
+ } catch (Throwable e) {
+ if (e instanceof InternalError || e.getCause() instanceof InternalError) {
+ System.out.println("Caught expected error from attempting to invoke an obsolete method.");
+ } else {
+ System.out.println("Unexpected error type for calling obsolete method! Expected either "
+ + "an InternalError or something that is caused by an InternalError.");
+ throw new Error("Unexpected error caught: ", e);
+ }
+ }
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ // Gets the first obsolete method on the current threads stack (NB only looks through the first 30
+ // stack frames).
+ private static native Method getFirstObsoleteMethod984();
+}
diff --git a/test/984-obsolete-invoke/src/Transform.java b/test/984-obsolete-invoke/src/Transform.java
new file mode 100644
index 0000000..536de84
--- /dev/null
+++ b/test/984-obsolete-invoke/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 {
+ // This method must be 'static' so that when we try to invoke it through a j.l.r.Method we will
+ // simply use the jmethodID directly and not do any lookup in any receiver object.
+ public static void sayHi(Runnable r) {
+ System.out.println("hello");
+ r.run();
+ System.out.println("goodbye");
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 40f7edd..b79006f 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -298,6 +298,7 @@
"945-obsolete-native/obsolete_native.cc",
"980-redefine-object/redefine_object.cc",
"983-source-transform-verify/source_transform.cc",
+ "984-obsolete-invoke/obsolete_invoke.cc",
],
}
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 282ac48..0ab50af 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -16,14 +16,14 @@
"""Build and run go/ab/git_master-art-host target
+This script is executed by the android build server and must not be moved,
+or changed in an otherwise backwards-incompatible manner.
+
Provided with a target name, the script setup the environment for
building the test target by taking config information from
from target_config.py.
-If the target field is defined in the configuration for the target, it
-invokes `make` to build the target, otherwise, it assumes
-that the its is a run-test target, and invokes testrunner.py
-script for building and running the run-tests.
+See target_config.py for the configuration syntax.
"""
import argparse
@@ -35,10 +35,26 @@
import env
parser = argparse.ArgumentParser()
-parser.add_argument('build_target')
parser.add_argument('-j', default='1', dest='n_threads')
+# either -l/--list OR build-target is required (but not both).
+group = parser.add_mutually_exclusive_group(required=True)
+group.add_argument('build_target', nargs='?')
+group.add_argument('-l', '--list', action='store_true', help='List all possible run-build targets.')
options = parser.parse_args()
+##########
+
+if options.list:
+ print "List of all known build_target: "
+ for k in sorted(target_config.iterkeys()):
+ print " * " + k
+ # TODO: would be nice if this was the same order as the target config file.
+ sys.exit(1)
+
+if not target_config.get(options.build_target):
+ sys.stderr.write("error: invalid build_target, see -l/--list.\n")
+ sys.exit(1)
+
target = target_config[options.build_target]
n_threads = options.n_threads
custom_env = target.get('env', {})
@@ -46,28 +62,46 @@
print custom_env
os.environ.update(custom_env)
-if target.get('target'):
+if target.get('make'):
build_command = 'make'
build_command += ' -j' + str(n_threads)
build_command += ' -C ' + env.ANDROID_BUILD_TOP
- build_command += ' ' + target.get('target')
+ build_command += ' ' + target.get('make')
# Add 'dist' to avoid Jack issues b/36169180.
build_command += ' dist'
- sys.stdout.write(str(build_command))
+ sys.stdout.write(str(build_command) + '\n')
sys.stdout.flush()
if subprocess.call(build_command.split()):
sys.exit(1)
-if target.get('run-tests'):
+if target.get('golem'):
+ machine_type = target.get('golem')
+ # use art-opt-cc by default since it mimics the default preopt config.
+ default_golem_config = 'art-opt-cc'
+
+ os.chdir(env.ANDROID_BUILD_TOP)
+ cmd = ['art/tools/golem/build-target.sh']
+ cmd += ['-j' + str(n_threads)]
+ cmd += ['--showcommands']
+ cmd += ['--machine-type=%s' %(machine_type)]
+ cmd += ['--golem=%s' %(default_golem_config)]
+ cmd += ['--tarball']
+ sys.stdout.write(str(cmd) + '\n')
+ sys.stdout.flush()
+
+ if subprocess.call(cmd):
+ sys.exit(1)
+
+if target.get('run-test'):
run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
- run_test_command += target.get('flags', [])
+ run_test_command += target.get('run-test', [])
run_test_command += ['-j', str(n_threads)]
run_test_command += ['-b']
run_test_command += ['--host']
run_test_command += ['--verbose']
- sys.stdout.write(str(run_test_command))
+ sys.stdout.write(str(run_test_command) + '\n')
sys.stdout.flush()
if subprocess.call(run_test_command):
sys.exit(1)
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 5a6ecff..82f7832 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -1,72 +1,86 @@
target_config = {
+
+# Configuration syntax:
+#
+# Required keys: (Use one or more of these)
+# * golem - specify a golem machine-type to build, e.g. android-armv8
+# (uses art/tools/golem/build-target.sh)
+# * make - specify a make target to build, e.g. build-art-host
+# * run-test - runs the tests in art/test/ directory with testrunner.py,
+# specify a list of arguments to pass to testrunner.py
+#
+# Optional keys: (Use any of these)
+# * env - Add additional environment variable to the current environment.
+#
+# *** IMPORTANT ***:
+# This configuration is used by the android build server. Targets must not be renamed
+# or removed.
+#
+
+##########################################
+
+ # ART run-test configurations
+ # (calls testrunner which builds and then runs the test targets)
+
'art-test' : {
- 'target' : 'test-art-host-gtest',
- 'run-tests' : True,
- 'flags' : [],
+ 'make' : 'test-art-host-gtest',
+ 'run-test' : [],
'env' : {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-interpreter' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter'],
+ 'run-test' : ['--interpreter'],
'env' : {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-interpreter-access-checks' : {
- 'run-tests' : True,
- 'flags' : ['--interp-ac'],
+ 'run-test' : ['--interp-ac'],
'env' : {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-jit' : {
- 'run-tests' : True,
- 'flags' : ['--jit'],
+ 'run-test' : ['--jit'],
'env' : {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gcstress-gcverify': {
- 'run-tests' : True,
- 'flags' : ['--gcstress',
- '--gcverify'],
+ 'run-test': ['--gcstress',
+ '--gcverify'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
'ART_DEFAULT_GC_TYPE' : 'SS'
}
},
'art-interpreter-gcstress' : {
- 'run-tests' : True,
- 'flags': ['--interpreter',
- '--gcstress'],
+ 'run-test' : ['--interpreter',
+ '--gcstress'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
'ART_DEFAULT_GC_TYPE' : 'SS'
}
},
'art-optimizing-gcstress' : {
- 'run-tests' : True,
- 'flags': ['--gcstress',
- '--optimizing'],
+ 'run-test' : ['--gcstress',
+ '--optimizing'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
'ART_DEFAULT_GC_TYPE' : 'SS'
}
},
'art-jit-gcstress' : {
- 'run-tests' : True,
- 'flags': ['--jit',
- '--gcstress'],
+ 'run-test' : ['--jit',
+ '--gcstress'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
'ART_DEFAULT_GC_TYPE' : 'SS'
}
},
'art-read-barrier' : {
- 'run-tests' : True,
- 'flags': ['--interpreter',
+ 'run-test': ['--interpreter',
'--optimizing'],
'env' : {
'ART_USE_READ_BARRIER' : 'true',
@@ -74,19 +88,17 @@
}
},
'art-read-barrier-gcstress' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing',
- '--gcstress'],
+ 'run-test' : ['--interpreter',
+ '--optimizing',
+ '--gcstress'],
'env' : {
'ART_USE_READ_BARRIER' : 'true',
'ART_HEAP_POISONING' : 'true'
}
},
'art-read-barrier-table-lookup' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing'],
+ 'run-test' : ['--interpreter',
+ '--optimizing'],
'env' : {
'ART_USE_READ_BARRIER' : 'true',
'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP',
@@ -94,39 +106,35 @@
}
},
'art-debug-gc' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing'],
+ 'run-test' : ['--interpreter',
+ '--optimizing'],
'env' : {
'ART_TEST_DEBUG_GC' : 'true',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-ss-gc' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing',
- '--jit'],
+ 'run-test' : ['--interpreter',
+ '--optimizing',
+ '--jit'],
'env' : {
'ART_DEFAULT_GC_TYPE' : 'SS',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gss-gc' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing',
- '--jit'],
+ 'run-test' : ['--interpreter',
+ '--optimizing',
+ '--jit'],
'env' : {
'ART_DEFAULT_GC_TYPE' : 'GSS',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-ss-gc-tlab' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing',
- '--jit'],
+ 'run-test' : ['--interpreter',
+ '--optimizing',
+ '--jit'],
'env' : {
'ART_DEFAULT_GC_TYPE' : 'SS',
'ART_USE_TLAB' : 'true',
@@ -134,10 +142,9 @@
}
},
'art-gss-gc-tlab' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing',
- '--jit'],
+ 'run-test' : ['--interpreter',
+ '--optimizing',
+ '--jit'],
'env' : {
'ART_DEFAULT_GC_TYPE' : 'GSS',
'ART_USE_TLAB' : 'true',
@@ -145,87 +152,82 @@
}
},
'art-tracing' : {
- 'run-tests' : True,
- 'flags' : ['--trace'],
+ 'run-test' : ['--trace'],
'env' : {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-interpreter-tracing' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--trace'],
+ 'run-test' : ['--interpreter',
+ '--trace'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-forcecopy' : {
- 'run-tests' : True,
- 'flags' : ['--forcecopy'],
+ 'run-test' : ['--forcecopy'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-no-prebuild' : {
- 'run-tests' : True,
- 'flags' : ['--no-prebuild'],
+ 'run-test' : ['--no-prebuild'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-no-image' : {
- 'run-tests' : True,
- 'flags' : ['--no-image'],
+ 'run-test' : ['--no-image'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-interpreter-no-image' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--no-image'],
+ 'run-test' : ['--interpreter',
+ '--no-image'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-relocate-no-patchoat' : {
- 'run-tests' : True,
- 'flags' : ['--relocate-npatchoat'],
+ 'run-test' : ['--relocate-npatchoat'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-no-dex2oat' : {
- 'run-tests' : True,
- 'flags' : ['--no-dex2oat'],
+ 'run-test' : ['--no-dex2oat'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
}
},
'art-heap-poisoning' : {
- 'run-tests' : True,
- 'flags' : ['--interpreter',
- '--optimizing'],
+ 'run-test' : ['--interpreter',
+ '--optimizing'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
'ART_HEAP_POISONING' : 'true'
}
},
+
+ # ART gtest configurations
+ # (calls make 'target' which builds and then runs the gtests).
+
'art-gtest' : {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env' : {
'ART_USE_READ_BARRIER' : 'true'
}
},
'art-gtest-read-barrier': {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env' : {
'ART_USE_READ_BARRIER' : 'true',
'ART_HEAP_POISONING' : 'true'
}
},
'art-gtest-read-barrier-table-lookup': {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env': {
'ART_USE_READ_BARRIER' : 'true',
'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP',
@@ -233,21 +235,21 @@
}
},
'art-gtest-ss-gc': {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env': {
'ART_DEFAULT_GC_TYPE' : 'SS',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-gss-gc': {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env' : {
'ART_DEFAULT_GC_TYPE' : 'GSS',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-ss-gc-tlab': {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env': {
'ART_DEFAULT_GC_TYPE' : 'SS',
'ART_USE_TLAB' : 'true',
@@ -255,7 +257,7 @@
}
},
'art-gtest-gss-gc-tlab': {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env': {
'ART_DEFAULT_GC_TYPE' : 'GSS',
'ART_USE_TLAB' : 'true',
@@ -263,29 +265,54 @@
}
},
'art-gtest-debug-gc' : {
- 'target' : 'test-art-host-gtest',
+ 'make' : 'test-art-host-gtest',
'env' : {
'ART_TEST_DEBUG_GC' : 'true',
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-valgrind32': {
- 'target' : 'valgrind-test-art-host32',
+ 'make' : 'valgrind-test-art-host32',
'env': {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-valgrind64': {
- 'target' : 'valgrind-test-art-host64',
+ 'make' : 'valgrind-test-art-host64',
'env': {
'ART_USE_READ_BARRIER' : 'false'
}
},
'art-gtest-heap-poisoning': {
- 'target' : 'valgrind-test-art-host64',
+ 'make' : 'valgrind-test-art-host64',
'env' : {
'ART_HEAP_POISONING' : 'true',
'ART_USE_READ_BARRIER' : 'false'
}
- }
+ },
+
+ # ART Golem build targets used by go/lem (continuous ART benchmarking),
+ # (art-opt-cc is used by default since it mimics the default preopt config),
+ #
+ # calls golem/build-target.sh which builds a golem tarball of the target name,
+ # e.g. 'golem: android-armv7' produces an 'android-armv7.tar.gz' upon success.
+
+ 'art-golem-android-armv7': {
+ 'golem' : 'android-armv7'
+ },
+ 'art-golem-android-armv8': {
+ 'golem' : 'android-armv8'
+ },
+ 'art-golem-linux-armv7': {
+ 'golem' : 'linux-armv7'
+ },
+ 'art-golem-linux-armv8': {
+ 'golem' : 'linux-armv8'
+ },
+ 'art-golem-linux-ia32': {
+ 'golem' : 'linux-ia32'
+ },
+ 'art-golem-linux-x64': {
+ 'golem' : 'linux-x64'
+ },
}