summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--compiler/Android.bp3
-rw-r--r--compiler/dex/dex_to_dex_decompiler_test.cc6
-rw-r--r--compiler/dex/verification_results.cc5
-rw-r--r--compiler/driver/compiler_driver.cc60
-rw-r--r--compiler/driver/compiler_driver.h10
-rw-r--r--compiler/driver/compiler_driver_test.cc5
-rw-r--r--compiler/driver/compiler_options.cc2
-rw-r--r--compiler/image_writer.cc21
-rw-r--r--compiler/oat_writer.cc9
-rw-r--r--compiler/optimizing/codegen_test_utils.h1
-rw-r--r--compiler/optimizing/inliner.cc9
-rw-r--r--compiler/optimizing/inliner.h2
-rw-r--r--compiler/optimizing/reference_type_propagation.cc2
-rw-r--r--compiler/optimizing/reference_type_propagation.h2
-rw-r--r--compiler/utils/mips/assembler_mips.cc96
-rw-r--r--compiler/utils/mips/assembler_mips.h13
-rw-r--r--compiler/utils/mips/assembler_mips32r6_test.cc60
-rw-r--r--compiler/utils/mips64/assembler_mips64.cc60
-rw-r--r--compiler/utils/mips64/assembler_mips64.h13
-rw-r--r--compiler/utils/mips64/assembler_mips64_test.cc60
-rw-r--r--compiler/verifier_deps_test.cc12
-rw-r--r--dex2oat/Android.bp9
-rw-r--r--dex2oat/dex2oat.cc144
-rw-r--r--disassembler/disassembler_mips.cc4
-rw-r--r--imgdiag/imgdiag.cc22
-rw-r--r--openjdkjvm/Android.bp (renamed from runtime/openjdkjvm/Android.bp)1
-rw-r--r--openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION (renamed from runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION)0
-rw-r--r--openjdkjvm/NOTICE (renamed from runtime/openjdkjvm/NOTICE)0
-rw-r--r--openjdkjvm/OpenjdkJvm.cc (renamed from runtime/openjdkjvm/OpenjdkJvm.cc)0
-rw-r--r--runtime/Android.bp4
-rw-r--r--runtime/aot_class_linker.cc58
-rw-r--r--runtime/aot_class_linker.h40
-rw-r--r--runtime/art_method-inl.h39
-rw-r--r--runtime/art_method.cc2
-rw-r--r--runtime/art_method.h17
-rw-r--r--runtime/class_linker-inl.h11
-rw-r--r--runtime/class_linker.cc180
-rw-r--r--runtime/class_linker.h34
-rw-r--r--runtime/class_loader_context.cc29
-rw-r--r--runtime/class_loader_context_test.cc41
-rw-r--r--runtime/class_table-inl.h7
-rw-r--r--runtime/class_table.h6
-rw-r--r--runtime/debugger.cc4
-rw-r--r--runtime/dex_file_annotations.cc26
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc4
-rw-r--r--runtime/instrumentation.h7
-rw-r--r--runtime/interpreter/interpreter_common.cc2
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc2
-rw-r--r--runtime/jit/jit_code_cache.cc4
-rw-r--r--runtime/native/java_lang_reflect_Executable.cc2
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc116
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h8
-rw-r--r--runtime/openjdkjvmti/events-inl.h10
-rw-r--r--runtime/openjdkjvmti/events.cc15
-rw-r--r--runtime/openjdkjvmti/events.h1
-rw-r--r--runtime/openjdkjvmti/ti_method.cc643
-rw-r--r--runtime/openjdkjvmti/ti_method.h27
-rw-r--r--runtime/openjdkjvmti/ti_thread.cc247
-rw-r--r--runtime/openjdkjvmti/ti_thread.h11
-rw-r--r--runtime/reflection.cc5
-rw-r--r--runtime/runtime.cc91
-rw-r--r--runtime/runtime.h23
-rw-r--r--runtime/transaction.cc48
-rw-r--r--runtime/transaction.h13
-rw-r--r--runtime/verifier/method_verifier.cc18
-rw-r--r--runtime/verifier/verifier_deps.cc8
-rw-r--r--runtime/verifier/verifier_deps.h9
-rw-r--r--simulator/Android.bp (renamed from runtime/simulator/Android.bp)45
-rw-r--r--simulator/code_simulator.cc (renamed from runtime/simulator/code_simulator.cc)5
-rw-r--r--simulator/code_simulator_arm64.cc (renamed from runtime/simulator/code_simulator_arm64.cc)2
-rw-r--r--simulator/code_simulator_arm64.h (renamed from runtime/simulator/code_simulator_arm64.h)9
-rw-r--r--simulator/code_simulator_container.cc (renamed from runtime/code_simulator_container.cc)2
-rw-r--r--simulator/code_simulator_container.h (renamed from runtime/code_simulator_container.h)9
-rw-r--r--simulator/include/code_simulator.h (renamed from runtime/simulator/code_simulator.h)6
-rw-r--r--test/1911-get-local-var-table/expected.txt0
-rw-r--r--test/1911-get-local-var-table/info.txt1
-rwxr-xr-xtest/1911-get-local-var-table/run18
-rw-r--r--test/1911-get-local-var-table/src/Main.java21
-rw-r--r--test/1911-get-local-var-table/src/art/Breakpoint.java202
-rw-r--r--test/1911-get-local-var-table/src/art/Locals.java121
-rw-r--r--test/1911-get-local-var-table/src/art/Suspension.java30
-rw-r--r--test/1911-get-local-var-table/src/art/Test1911.java218
-rw-r--r--test/1912-get-set-local-primitive/expected.txt108
-rw-r--r--test/1912-get-set-local-primitive/info.txt2
-rwxr-xr-xtest/1912-get-set-local-primitive/run18
-rw-r--r--test/1912-get-set-local-primitive/src/Main.java21
-rw-r--r--test/1912-get-set-local-primitive/src/art/Breakpoint.java202
-rw-r--r--test/1912-get-set-local-primitive/src/art/Locals.java121
-rw-r--r--test/1912-get-set-local-primitive/src/art/StackTrace.java68
-rw-r--r--test/1912-get-set-local-primitive/src/art/Suspension.java30
-rw-r--r--test/1912-get-set-local-primitive/src/art/Test1912.java260
-rw-r--r--test/1913-get-set-local-objects/expected.txt72
-rw-r--r--test/1913-get-set-local-objects/info.txt2
-rwxr-xr-xtest/1913-get-set-local-objects/run18
-rw-r--r--test/1913-get-set-local-objects/src/Main.java21
-rw-r--r--test/1913-get-set-local-objects/src/art/Breakpoint.java202
-rw-r--r--test/1913-get-set-local-objects/src/art/Locals.java121
-rw-r--r--test/1913-get-set-local-objects/src/art/StackTrace.java68
-rw-r--r--test/1913-get-set-local-objects/src/art/Suspension.java30
-rw-r--r--test/1913-get-set-local-objects/src/art/Test1913.java251
-rw-r--r--test/1914-get-local-instance/expected.txt12
-rw-r--r--test/1914-get-local-instance/info.txt2
-rw-r--r--test/1914-get-local-instance/local_instance.cc68
-rwxr-xr-xtest/1914-get-local-instance/run18
-rw-r--r--test/1914-get-local-instance/src/Main.java21
-rw-r--r--test/1914-get-local-instance/src/art/Breakpoint.java202
-rw-r--r--test/1914-get-local-instance/src/art/Locals.java121
-rw-r--r--test/1914-get-local-instance/src/art/StackTrace.java68
-rw-r--r--test/1914-get-local-instance/src/art/Suspension.java30
-rw-r--r--test/1914-get-local-instance/src/art/Test1914.java182
-rw-r--r--test/1915-get-set-local-current-thread/expected.txt5
-rw-r--r--test/1915-get-set-local-current-thread/info.txt2
-rwxr-xr-xtest/1915-get-set-local-current-thread/run18
-rw-r--r--test/1915-get-set-local-current-thread/src/Main.java21
-rw-r--r--test/1915-get-set-local-current-thread/src/art/Breakpoint.java202
-rw-r--r--test/1915-get-set-local-current-thread/src/art/Locals.java121
-rw-r--r--test/1915-get-set-local-current-thread/src/art/StackTrace.java68
-rw-r--r--test/1915-get-set-local-current-thread/src/art/Suspension.java30
-rw-r--r--test/1915-get-set-local-current-thread/src/art/Test1915.java105
-rw-r--r--test/1916-get-set-current-frame/expected.txt4
-rw-r--r--test/1916-get-set-current-frame/info.txt2
-rwxr-xr-xtest/1916-get-set-current-frame/run18
-rw-r--r--test/1916-get-set-current-frame/src/Main.java21
-rw-r--r--test/1916-get-set-current-frame/src/art/Breakpoint.java202
-rw-r--r--test/1916-get-set-current-frame/src/art/Locals.java121
-rw-r--r--test/1916-get-set-current-frame/src/art/StackTrace.java68
-rw-r--r--test/1916-get-set-current-frame/src/art/Suspension.java30
-rw-r--r--test/1916-get-set-current-frame/src/art/Test1916.java148
-rw-r--r--test/1917-get-stack-frame/expected.txt33
-rw-r--r--test/1917-get-stack-frame/info.txt1
-rwxr-xr-xtest/1917-get-stack-frame/run18
-rw-r--r--test/1917-get-stack-frame/src/Main.java21
-rw-r--r--test/1917-get-stack-frame/src/art/Breakpoint.java202
-rw-r--r--test/1917-get-stack-frame/src/art/StackTrace.java67
-rw-r--r--test/1917-get-stack-frame/src/art/Suspension.java30
-rw-r--r--test/1917-get-stack-frame/src/art/Test1917.java150
-rw-r--r--test/550-checker-multiply-accumulate/src/Main.java4
-rw-r--r--test/616-cha/src/Main.java7
-rw-r--r--test/Android.bp3
-rwxr-xr-xtest/etc/run-test-jar4
-rw-r--r--test/knownfailures.json12
-rw-r--r--test/ti-agent/jvmti_helper.cc14
-rw-r--r--test/ti-agent/jvmti_helper.h2
-rw-r--r--test/ti-agent/locals_helper.cc210
-rw-r--r--test/ti-agent/stack_trace_helper.cc99
-rw-r--r--tools/dexfuzz/README2
-rw-r--r--tools/dexfuzz/src/dexfuzz/DexFuzz.java4
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java28
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java3
-rwxr-xr-xtools/generate_cmake_lists.py98
-rw-r--r--tools/jfuzz/jfuzz.cc9
-rwxr-xr-xtools/jfuzz/run_jfuzz_test.py4
153 files changed, 7117 insertions, 567 deletions
diff --git a/Android.bp b/Android.bp
index d0e22fb873..cb72082e6d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,10 +31,12 @@ subdirs = [
"disassembler",
"imgdiag",
"oatdump",
+ "openjdkjvm",
"patchoat",
"profman",
"runtime",
"sigchainlib",
+ "simulator",
"test",
"tools/cpp-define-generator",
"tools/dmtracedump",
diff --git a/compiler/Android.bp b/compiler/Android.bp
index b721d210fe..f11d25675c 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -423,8 +423,11 @@ art_cc_test {
},
},
+ header_libs: ["libart_simulator_headers"],
+
shared_libs: [
"libartd-compiler",
+ "libartd-simulator-container",
"libvixld-arm",
"libvixld-arm64",
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 7b56f3ec1a..1ef3ba7c00 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -29,6 +29,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "verifier/method_verifier-inl.h"
+#include "verifier/verifier_deps.h"
namespace art {
@@ -39,6 +40,11 @@ class DexToDexDecompilerTest : public CommonCompilerTest {
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
compiler_options_->boot_image_ = false;
compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
+ // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
+ // the results for all the dex files, not just the results for the current dex file.
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+ new verifier::VerifierDeps(GetDexFiles(class_loader)));
+ compiler_driver_->SetDexFilesForOatFile(GetDexFiles(class_loader));
compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
}
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index e657e3bc86..cfb56e30a8 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -46,10 +46,6 @@ VerificationResults::~VerificationResults() {
void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
DCHECK(method_verifier != nullptr);
- if (!compiler_options_->IsAnyCompilationEnabled()) {
- // Verified methods are only required for quickening and compilation.
- return;
- }
MethodReference ref = method_verifier->GetMethodReference();
std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
if (verified_method == nullptr) {
@@ -104,7 +100,6 @@ void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method
const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
const VerifiedMethod* ret = nullptr;
- DCHECK(compiler_options_->IsAnyCompilationEnabled());
if (atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &ret)) {
return ret;
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index ed36e111ff..0b1bce62c9 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -303,7 +303,6 @@ CompilerDriver::CompilerDriver(
timings_logger_(timer),
compiler_context_(nullptr),
support_boot_image_fixup_(true),
- dex_files_for_oat_file_(nullptr),
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info),
max_arena_alloc_(0),
@@ -924,8 +923,11 @@ void CompilerDriver::PreCompile(jobject class_loader,
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
- LOG(FATAL) << "Had a hard failure verifying all classes, and was asked to abort in such "
- << "situations. Please check the log.";
+ // Avoid dumping threads. Even if we shut down the thread pools, there will still be three
+ // instances of this thread's stack.
+ LOG(FATAL_WITHOUT_ABORT) << "Had a hard failure verifying all classes, and was asked to abort "
+ << "in such situations. Please check the log.";
+ abort();
}
if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -1912,8 +1914,8 @@ bool CompilerDriver::FastVerify(jobject jclass_loader,
TimingLogger* timings) {
verifier::VerifierDeps* verifier_deps =
Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
- // If there is an existing `VerifierDeps`, try to use it for fast verification.
- if (verifier_deps == nullptr) {
+ // If there exist VerifierDeps that aren't the ones we just created to output, use them to verify.
+ if (verifier_deps == nullptr || verifier_deps->OutputOnly()) {
return false;
}
TimingLogger::ScopedTiming t("Fast Verify", timings);
@@ -1980,13 +1982,6 @@ bool CompilerDriver::FastVerify(jobject jclass_loader,
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
- // Always add the dex files to compiled_classes_. This happens for all compiler filters.
- for (const DexFile* dex_file : dex_files) {
- if (!compiled_classes_.HaveDexFile(dex_file)) {
- compiled_classes_.AddDexFile(dex_file, dex_file->NumClassDefs());
- }
- }
-
if (FastVerify(jclass_loader, dex_files, timings)) {
return;
}
@@ -1996,14 +1991,16 @@ void CompilerDriver::Verify(jobject jclass_loader,
// non boot image compilation. The verifier will need it to record the new dependencies.
// Then dex2oat can update the vdex file with these new dependencies.
if (!GetCompilerOptions().IsBootImage()) {
+ // Dex2oat creates the verifier deps.
// Create the main VerifierDeps, and set it to this thread.
- verifier::VerifierDeps* verifier_deps = new verifier::VerifierDeps(dex_files);
- Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps);
+ verifier::VerifierDeps* verifier_deps =
+ Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+ CHECK(verifier_deps != nullptr);
Thread::Current()->SetVerifierDeps(verifier_deps);
// Create per-thread VerifierDeps to avoid contention on the main one.
// We will merge them after verification.
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
- worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files));
+ worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files_for_oat_file_));
}
}
@@ -2028,7 +2025,7 @@ void CompilerDriver::Verify(jobject jclass_loader,
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
worker->GetThread()->SetVerifierDeps(nullptr);
- verifier_deps->MergeWith(*thread_deps, dex_files);;
+ verifier_deps->MergeWith(*thread_deps, dex_files_for_oat_file_);
delete thread_deps;
}
Thread::Current()->SetVerifierDeps(nullptr);
@@ -2332,7 +2329,7 @@ class InitializeClassVisitor : public CompilationVisitor {
// checks in Thread::AssertThreadSuspensionIsAllowable.
Runtime* const runtime = Runtime::Current();
// Run the class initializer in transaction mode.
- runtime->EnterTransactionMode(klass.Get());
+ runtime->EnterTransactionMode(is_app_image, klass.Get());
bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
true);
// TODO we detach transaction from runtime to indicate we quit the transactional
@@ -2359,7 +2356,7 @@ class InitializeClassVisitor : public CompilationVisitor {
*file_log << exception->Dump() << "\n";
}
soa.Self()->ClearException();
- runtime->RollbackAndExitTransactionMode();
+ runtime->RollbackAllTransactions();
CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
} else if (is_boot_image) {
// For boot image, we want to put the updated status in the oat class since we can't
@@ -2452,7 +2449,8 @@ class InitializeClassVisitor : public CompilationVisitor {
bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally.
+ // Return value of ResolveReturnType() is discarded because resolve will be done internally.
+ ObjPtr<mirror::Class> rtn_type = m->ResolveReturnType();
if (rtn_type == nullptr) {
self->ClearException();
return false;
@@ -2461,7 +2459,7 @@ class InitializeClassVisitor : public CompilationVisitor {
if (types != nullptr) {
for (uint32_t i = 0; i < types->Size(); ++i) {
dex::TypeIndex param_type_idx = types->GetTypeItem(i).type_idx_;
- auto param_type = m->GetClassFromTypeIndex(param_type_idx, true);
+ ObjPtr<mirror::Class> param_type = m->ResolveClassFromTypeIndex(param_type_idx);
if (param_type == nullptr) {
self->ClearException();
return false;
@@ -2698,7 +2696,14 @@ void CompilerDriver::Compile(jobject class_loader,
: profile_compilation_info_->DumpInfo(&dex_files));
}
- DCHECK(current_dex_to_dex_methods_ == nullptr);
+ current_dex_to_dex_methods_ = nullptr;
+ Thread* const self = Thread::Current();
+ {
+ // Clear in case we aren't the first call to Compile.
+ MutexLock mu(self, dex_to_dex_references_lock_);
+ dex_to_dex_references_.clear();
+ }
+
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
CompileDexFile(class_loader,
@@ -2717,7 +2722,7 @@ void CompilerDriver::Compile(jobject class_loader,
{
// From this point on, we shall not modify dex_to_dex_references_, so
// just grab a reference to it that we use without holding the mutex.
- MutexLock lock(Thread::Current(), dex_to_dex_references_lock_);
+ MutexLock lock(self, dex_to_dex_references_lock_);
dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
}
for (const auto& method_set : dex_to_dex_references) {
@@ -2910,7 +2915,7 @@ void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status
if (kIsDebugBuild) {
// Check to make sure it's not a dex file for an oat file we are compiling since these
// should always succeed. These do not include classes in for used libraries.
- for (const DexFile* dex_file : *dex_files_for_oat_file_) {
+ for (const DexFile* dex_file : GetDexFilesForOatFile()) {
CHECK_NE(dex_ref.dex_file, dex_file) << dex_ref.dex_file->GetLocation();
}
}
@@ -3028,4 +3033,13 @@ void CompilerDriver::FreeThreadPools() {
single_thread_pool_.reset();
}
+void CompilerDriver::SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
+ dex_files_for_oat_file_ = dex_files;
+ for (const DexFile* dex_file : dex_files) {
+ if (!compiled_classes_.HaveDexFile(dex_file)) {
+ compiled_classes_.AddDexFile(dex_file, dex_file->NumClassDefs());
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index ecaed83e57..d9886a2fba 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -103,15 +103,11 @@ class CompilerDriver {
~CompilerDriver();
// Set dex files that will be stored in the oat file after being compiled.
- void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
- dex_files_for_oat_file_ = &dex_files;
- }
+ void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files);
// Get dex file that will be stored in the oat file after being compiled.
ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
- return (dex_files_for_oat_file_ != nullptr)
- ? ArrayRef<const DexFile* const>(*dex_files_for_oat_file_)
- : ArrayRef<const DexFile* const>();
+ return ArrayRef<const DexFile* const>(dex_files_for_oat_file_);
}
void CompileAll(jobject class_loader,
@@ -532,7 +528,7 @@ class CompilerDriver {
bool support_boot_image_fixup_;
// List of dex files that will be stored in the oat file.
- const std::vector<const DexFile*>* dex_files_for_oat_file_;
+ std::vector<const DexFile*> dex_files_for_oat_file_;
CompiledMethodStorage compiled_method_storage_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 10bfd972f0..fee6afb91f 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -42,7 +42,9 @@ class CompilerDriverTest : public CommonCompilerTest {
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
- compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
+ dex_files_ = GetDexFiles(class_loader);
+ compiler_driver_->SetDexFilesForOatFile(dex_files_);;
+ compiler_driver_->CompileAll(class_loader, dex_files_, &timings);
t.NewTiming("MakeAllExecutable");
MakeAllExecutable(class_loader);
}
@@ -95,6 +97,7 @@ class CompilerDriverTest : public CommonCompilerTest {
JNIEnv* env_;
jclass class_;
jmethodID mid_;
+ std::vector<const DexFile*> dex_files_;
};
// Disabled due to 10 second runtime on host
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 76f0ae9202..3cacc2cad7 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -144,6 +144,8 @@ bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usa
ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold_, Usage);
} else if (option == "--abort-on-hard-verifier-error") {
abort_on_hard_verifier_failure_ = true;
+ } else if (option == "--no-abort-on-hard-verifier-error") {
+ abort_on_hard_verifier_failure_ = false;
} else if (option.starts_with("--dump-init-failures=")) {
ParseDumpInitFailures(option, Usage);
} else if (option.starts_with("--dump-cfg=")) {
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 318009c606..fc7cd016b2 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -894,7 +894,7 @@ bool ImageWriter::PruneAppImageClassInternal(
&my_early_exit,
visited);
// Remove the class if the dex file is not in the set of dex files. This happens for classes that
- // are from uses library if there is no profile. b/30688277
+ // are from uses-library if there is no profile. b/30688277
mirror::DexCache* dex_cache = klass->GetDexCache();
if (dex_cache != nullptr) {
result = result ||
@@ -1153,9 +1153,22 @@ void ImageWriter::PruneNonImageClasses() {
Thread* self = Thread::Current();
ScopedAssertNoThreadSuspension sa(__FUNCTION__);
- // Clear class table strong roots so that dex caches can get pruned. We require pruning the class
- // path dex caches.
- class_linker->ClearClassTableStrongRoots();
+ // Prune uses-library dex caches. Only prune the uses-library dex caches since we want to make
+ // sure the other ones don't get unloaded before the OatWriter runs.
+ class_linker->VisitClassTables(
+ [&](ClassTable* table) REQUIRES_SHARED(Locks::mutator_lock_) {
+ table->RemoveStrongRoots(
+ [&](GcRoot<mirror::Object> root) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> obj = root.Read();
+ if (obj->IsDexCache()) {
+ // Return true if the dex file is not one of the ones in the map.
+ return dex_file_oat_index_map_.find(obj->AsDexCache()->GetDexFile()) ==
+ dex_file_oat_index_map_.end();
+ }
+ // Return false to avoid removing.
+ return false;
+ });
+ });
// Remove the undesired classes from the class roots.
ObjPtr<mirror::ClassLoader> class_loader;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 4d258af843..d7e3a28777 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1282,9 +1282,12 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE
REQUIRES_SHARED(Locks::mutator_lock_) {
OatDexMethodVisitor::StartClass(dex_file, class_def_index);
- if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
- dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file);
- DCHECK(dex_cache_ != nullptr);
+ if (writer_->GetCompilerDriver()->GetCompilerOptions().IsAotCompilationEnabled()) {
+ // Only need to set the dex cache if we have compilation. Other modes might have unloaded it.
+ if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
+ dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file);
+ DCHECK(dex_cache_ != nullptr);
+ }
}
return true;
}
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 1b38acd8b0..cada2e679b 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -28,6 +28,7 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "arch/x86/registers_x86.h"
#include "arch/x86_64/instruction_set_features_x86_64.h"
+#include "code_simulator.h"
#include "code_simulator_container.h"
#include "common_compiler_test.h"
#include "graph_checker.h"
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 38a1a8c024..0141c26352 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1948,7 +1948,7 @@ static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti,
declared_rti.IsStrictSupertypeOf(actual_rti);
}
-ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) {
+ReferenceTypeInfo HInliner::GetClassRTI(ObjPtr<mirror::Class> klass) {
return ReferenceTypePropagation::IsAdmissible(klass)
? ReferenceTypeInfo::Create(handles_->NewHandle(klass))
: graph_->GetInexactObjectRti();
@@ -1976,9 +1976,8 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod*
++param_idx, ++input_idx) {
HInstruction* input = invoke_instruction->InputAt(input_idx);
if (input->GetType() == Primitive::kPrimNot) {
- mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex(
- param_list->GetTypeItem(param_idx).type_idx_,
- /* resolve */ false);
+ ObjPtr<mirror::Class> param_cls = resolved_method->LookupResolvedClassFromTypeIndex(
+ param_list->GetTypeItem(param_idx).type_idx_);
if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
/* declared_can_be_null */ true,
input)) {
@@ -2027,7 +2026,7 @@ void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
// TODO: we could be more precise by merging the phi inputs but that requires
// some functionality from the reference type propagation.
DCHECK(return_replacement->IsPhi());
- mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */);
+ ObjPtr<mirror::Class> cls = resolved_method->LookupResolvedReturnType();
return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
}
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 62c6713208..c4b3a32d91 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -207,7 +207,7 @@ class HInliner : public HOptimization {
// Creates an instance of ReferenceTypeInfo from `klass` if `klass` is
// admissible (see ReferenceTypePropagation::IsAdmissible for details).
// Otherwise returns inexact Object RTI.
- ReferenceTypeInfo GetClassRTI(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ ReferenceTypeInfo GetClassRTI(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index f172e16ff9..561c9eafa2 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -848,7 +848,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) {
ScopedObjectAccess soa(Thread::Current());
ArtMethod* method = instr->GetResolvedMethod();
- mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false);
+ ObjPtr<mirror::Class> klass = (method == nullptr) ? nullptr : method->LookupResolvedReturnType();
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 215e96786b..b19f473e27 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -46,7 +46,7 @@ class ReferenceTypePropagation : public HOptimization {
// Returns true if klass is admissible to the propagation: non-null and resolved.
// For an array type, we also check if the component type is admissible.
- static bool IsAdmissible(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ static bool IsAdmissible(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
return klass != nullptr &&
klass->IsResolved() &&
(!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 24e34508d1..2cbabcfb32 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -2920,6 +2920,102 @@ void MipsAssembler::IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister w
static_cast<FRegister>(wt));
}
+void MipsAssembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst,
FRegister src,
bool is_double) {
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index e42bb3fa3d..a7ff931e7e 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -613,6 +613,19 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi
void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
// Helper for replicating floating point value in all destination elements.
void ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double);
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index 6ee2a5cb79..b72a14e906 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -1752,6 +1752,66 @@ TEST_F(AssemblerMIPS32r6Test, IlvrD) {
DriverStr(RepeatVVV(&mips::MipsAssembler::IlvrD, "ilvr.d ${reg1}, ${reg2}, ${reg3}"), "ilvr.d");
}
+TEST_F(AssemblerMIPS32r6Test, MaddvB) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvH) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvB) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvH) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmaddW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmaddD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmsubW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmsubD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.d");
+}
+
#undef __
} // namespace art
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 90398540f8..7a1beb656b 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1899,6 +1899,66 @@ void Mips64Assembler::IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister
EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14);
}
+void Mips64Assembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b);
+}
+
void Mips64Assembler::ReplicateFPToVectorRegister(VectorRegister dst,
FpuRegister src,
bool is_double) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 5e88033743..c39d120bce 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -796,6 +796,19 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer
void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
// Helper for replicating floating point value in all destination elements.
void ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double);
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index bdf9598ee7..021e335697 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -3340,6 +3340,66 @@ TEST_F(AssemblerMIPS64Test, IlvrD) {
"ilvr.d");
}
+TEST_F(AssemblerMIPS64Test, MaddvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmaddW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmaddD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmsubW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmsubD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.d");
+}
+
#undef __
} // namespace art
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 72e2a6ce9f..e9f3f8022d 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -87,13 +87,13 @@ class VerifierDepsTest : public CommonCompilerTest {
TimingLogger timings("Verify", false, false);
// The compiler driver handles the verifier deps in the callbacks, so
// remove what this class did for unit testing.
- verifier_deps_.reset(nullptr);
+ if (deps == nullptr) {
+ // Create some verifier deps by default if they are not already specified.
+ deps = new verifier::VerifierDeps(dex_files_);
+ verifier_deps_.reset(deps);
+ }
callbacks_->SetVerifierDeps(deps);
compiler_driver_->Verify(class_loader_, dex_files_, &timings);
- // The compiler driver may have updated the VerifierDeps in the callback object.
- if (callbacks_->GetVerifierDeps() != deps) {
- verifier_deps_.reset(callbacks_->GetVerifierDeps());
- }
callbacks_->SetVerifierDeps(nullptr);
// Clear entries in the verification results to avoid hitting a DCHECK that
// we always succeed inserting a new entry after verifying.
@@ -128,6 +128,7 @@ class VerifierDepsTest : public CommonCompilerTest {
for (const DexFile* dex_file : dex_files_) {
compiler_driver_->GetVerificationResults()->AddDexFile(dex_file);
}
+ compiler_driver_->SetDexFilesForOatFile(dex_files_);
}
void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1441,7 +1442,6 @@ TEST_F(VerifierDepsTest, CompilerDriver) {
ASSERT_FALSE(verifier_deps_ == nullptr);
ASSERT_FALSE(verifier_deps_->Equals(decoded_deps));
} else {
- ASSERT_TRUE(verifier_deps_ == nullptr);
VerifyClassStatus(decoded_deps);
}
}
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 346f5a7ef5..0d453efc0c 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -30,15 +30,6 @@ cc_defaults {
android: {
// Use the 32-bit version of dex2oat on devices
compile_multilib: "prefer32",
-
- sanitize: {
- // ASan slows down dex2oat by ~3.5x, which translates into
- // extremely slow first boot. Disabled to help speed up
- // SANITIZE_TARGET mode.
- // Bug: 22233158
- address: false,
- coverage: false,
- },
},
},
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3cc41a6b29..0826fa1488 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -65,8 +65,10 @@
#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
+#include "gc/verification.h"
#include "image_writer.h"
#include "interpreter/unstarted_runtime.h"
+#include "java_vm_ext.h"
#include "jit/profile_compilation_info.h"
#include "leb128.h"
#include "linker/buffered_output_stream.h"
@@ -593,7 +595,6 @@ class Dex2Oat FINAL {
passes_to_run_filename_(nullptr),
multi_image_(false),
is_host_(false),
- class_loader_(nullptr),
elf_writers_(),
oat_writers_(),
rodata_(),
@@ -1484,14 +1485,6 @@ class Dex2Oat FINAL {
}
}
- void Shutdown() {
- ScopedObjectAccess soa(Thread::Current());
- for (jobject dex_cache : dex_caches_) {
- soa.Env()->DeleteLocalRef(dex_cache);
- }
- dex_caches_.clear();
- }
-
void LoadClassProfileDescriptors() {
if (profile_compilation_info_ != nullptr && IsImage()) {
Runtime* runtime = Runtime::Current();
@@ -1660,6 +1653,8 @@ class Dex2Oat FINAL {
// If we need to downgrade the compiler-filter for size reasons.
if (!IsBootImage() && IsVeryLarge(dex_files_)) {
+ // If we need to downgrade the compiler-filter for size reasons, do that early before we read
+ // it below for creating verification callbacks.
if (!CompilerFilter::IsAsGoodAs(kLargeAppFilter, compiler_options_->GetCompilerFilter())) {
LOG(INFO) << "Very large app, downgrading to verify.";
// Note: this change won't be reflected in the key-value store, as that had to be
@@ -1712,13 +1707,11 @@ class Dex2Oat FINAL {
Thread* self = Thread::Current();
WellKnownClasses::Init(self->GetJniEnv());
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
if (!IsBootImage()) {
constexpr bool kSaveDexInput = false;
if (kSaveDexInput) {
SaveDexInput();
}
- class_loader_ = class_loader_context_->CreateClassLoader(dex_files_);
}
// Ensure opened dex files are writable for dex-to-dex transformations.
@@ -1729,24 +1722,12 @@ class Dex2Oat FINAL {
}
}
- // Ensure that the dex caches stay live since we don't want class unloading
- // to occur during compilation.
- for (const auto& dex_file : dex_files_) {
- ScopedObjectAccess soa(self);
- dex_caches_.push_back(soa.AddLocalReference<jobject>(
- class_linker->RegisterDexFile(*dex_file,
- soa.Decode<mirror::ClassLoader>(class_loader_).Ptr())));
- if (dex_caches_.back() == nullptr) {
- soa.Self()->AssertPendingException();
- soa.Self()->ClearException();
- PLOG(ERROR) << "Failed to register dex file.";
- return dex2oat::ReturnCode::kOther;
- }
- // Pre-register dex files so that we can access verification results without locks during
- // compilation and verification.
- if (verification_results_ != nullptr) {
- // Verification results are only required for modes that have any compilation. Avoid
- // adding the dex files if possible to prevent allocating large arrays.
+ // Verification results are only required for modes that have any compilation. Avoid
+ // adding the dex files if possible to prevent allocating large arrays.
+ if (verification_results_ != nullptr) {
+ for (const auto& dex_file : dex_files_) {
+ // Pre-register dex files so that we can access verification results without locks during
+ // compilation and verification.
verification_results_->AddDexFile(dex_file);
}
}
@@ -1759,13 +1740,50 @@ class Dex2Oat FINAL {
return IsImage() && oat_fd_ != kInvalidFd;
}
- // Create and invoke the compiler driver. This will compile all the dex files.
- void Compile() {
+ // Doesn't return the class loader since it's not meant to be used for image compilation.
+ void CompileDexFilesIndividually() {
+ CHECK(!IsImage()) << "Not supported with image";
+ for (const DexFile* dex_file : dex_files_) {
+ std::vector<const DexFile*> dex_files(1u, dex_file);
+ VLOG(compiler) << "Compiling " << dex_file->GetLocation();
+ jobject class_loader = CompileDexFiles(dex_files);
+ CHECK(class_loader != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ // Unload class loader to free RAM.
+ jweak weak_class_loader = soa.Env()->vm->AddWeakGlobalRef(
+ soa.Self(),
+ soa.Decode<mirror::ClassLoader>(class_loader));
+ soa.Env()->vm->DeleteGlobalRef(soa.Self(), class_loader);
+ runtime_->GetHeap()->CollectGarbage(/*clear_soft_references*/ true);
+ ObjPtr<mirror::ClassLoader> decoded_weak = soa.Decode<mirror::ClassLoader>(weak_class_loader);
+ if (decoded_weak != nullptr) {
+ LOG(FATAL) << "Failed to unload class loader, path from root set: "
+ << runtime_->GetHeap()->GetVerification()->FirstPathFromRootSet(decoded_weak);
+ }
+ VLOG(compiler) << "Unloaded classloader";
+ }
+ }
+
+ bool ShouldCompileDexFilesIndividually() const {
+ // Compile individually if we are not building an image, not using any compilation, and are
+ // using multidex.
+ // This means extract, verify, and quicken will use the individual compilation mode (to reduce
+ // RAM used by the compiler).
+ // TODO: Still do it for app images to get testing coverage. Note that this will generate empty
+ // app images.
+ return !IsImage() &&
+ dex_files_.size() > 1 &&
+ !CompilerFilter::IsAnyCompilationEnabled(compiler_options_->GetCompilerFilter());
+ }
+
+ // Set up and create the compiler driver and then invoke it to compile all the dex files.
+ jobject Compile() {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
// Find the dex files we should not inline from.
-
std::vector<std::string> no_inline_filters;
Split(no_inline_from_string_, ',', &no_inline_filters);
@@ -1776,7 +1794,6 @@ class Dex2Oat FINAL {
}
if (!no_inline_filters.empty()) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
std::vector<const DexFile*> class_path_files;
if (!IsBootImage()) {
// The class loader context is used only for apps.
@@ -1842,8 +1859,46 @@ class Dex2Oat FINAL {
// experimentation.
TimingLogger::ScopedTiming time_unquicken("Unquicken", timings_);
VdexFile::Unquicken(dex_files_, input_vdex_file_->GetQuickeningInfo());
+ } else {
+ // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
+ // the results for all the dex files, not just the results for the current dex file.
+ callbacks_->SetVerifierDeps(new verifier::VerifierDeps(dex_files_));
+ }
+ // Invoke the compilation.
+ if (ShouldCompileDexFilesIndividually()) {
+ CompileDexFilesIndividually();
+ // Return a null classloader since we already freed released it.
+ return nullptr;
}
- driver_->CompileAll(class_loader_, dex_files_, timings_);
+ return CompileDexFiles(dex_files_);
+ }
+
+ // Create the class loader, use it to compile, and return.
+ jobject CompileDexFiles(const std::vector<const DexFile*>& dex_files) {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
+ jobject class_loader = nullptr;
+ if (!IsBootImage()) {
+ class_loader = class_loader_context_->CreateClassLoader(dex_files_);
+ }
+
+ // Register dex caches and key them to the class loader so that they only unload when the
+ // class loader unloads.
+ for (const auto& dex_file : dex_files) {
+ ScopedObjectAccess soa(Thread::Current());
+ // Registering the dex cache adds a strong root in the class loader that prevents the dex
+ // cache from being unloaded early.
+ ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile(
+ *dex_file,
+ soa.Decode<mirror::ClassLoader>(class_loader));
+ if (dex_cache == nullptr) {
+ soa.Self()->AssertPendingException();
+ LOG(FATAL) << "Failed to register dex file " << dex_file->GetLocation() << " "
+ << soa.Self()->GetException()->Dump();
+ }
+ }
+ driver_->CompileAll(class_loader, dex_files, timings_);
+ return class_loader;
}
// Notes on the interleaving of creating the images and oat files to
@@ -2800,8 +2855,6 @@ class Dex2Oat FINAL {
// Dex files we are compiling, does not include the class path dex files.
std::vector<const DexFile*> dex_files_;
std::string no_inline_from_string_;
- std::vector<jobject> dex_caches_;
- jobject class_loader_;
std::vector<std::unique_ptr<ElfWriter>> elf_writers_;
std::vector<std::unique_ptr<OatWriter>> oat_writers_;
@@ -2870,9 +2923,23 @@ static void b13564922() {
#endif
}
+class ScopedGlobalRef {
+ public:
+ explicit ScopedGlobalRef(jobject obj) : obj_(obj) {}
+ ~ScopedGlobalRef() {
+ if (obj_ != nullptr) {
+ ScopedObjectAccess soa(Thread::Current());
+ soa.Env()->vm->DeleteGlobalRef(soa.Self(), obj_);
+ }
+ }
+
+ private:
+ jobject obj_;
+};
+
static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) {
dex2oat.LoadClassProfileDescriptors();
- dex2oat.Compile();
+ ScopedGlobalRef class_loader(dex2oat.Compile());
if (!dex2oat.WriteOutputFiles()) {
dex2oat.EraseOutputFiles();
@@ -2920,7 +2987,7 @@ static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) {
}
static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) {
- dex2oat.Compile();
+ ScopedGlobalRef class_loader(dex2oat.Compile());
if (!dex2oat.WriteOutputFiles()) {
dex2oat.EraseOutputFiles();
@@ -3014,7 +3081,6 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) {
result = CompileApp(*dex2oat);
}
- dex2oat->Shutdown();
return result;
}
} // namespace art
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 7cb216e766..1a395a45d2 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -477,6 +477,10 @@ static const MipsInstruction gMipsInstructions[] = {
{ kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" },
{ kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" },
{ kMsaMask | (0x7 << 23), kMsa | (0x5 << 23) | 0x14, "ilvr", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0x12, "maddv", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x2 << 23) | 0x12, "msubv", "Vkmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x4 << 22) | 0x1b, "fmadd", "Ukmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x5 << 22) | 0x1b, "fmsub", "Ukmn" },
};
static uint32_t ReadU32(const uint8_t* ptr) {
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index fb8e894581..7ef24c700e 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -1075,6 +1075,8 @@ class ImgDiagDumper {
}
}
+ std::vector<size_t> private_dirty_pages_for_section(ImageHeader::kSectionCount, 0u);
+
// Iterate through one byte at a time.
ptrdiff_t page_off_begin = image_header_.GetImageBegin() - image_begin;
for (uintptr_t begin = boot_map_.start; begin != boot_map_.end; ++begin) {
@@ -1127,6 +1129,12 @@ class ImgDiagDumper {
if (is_dirty && is_private) {
mapping_data->private_dirty_pages++;
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
+ if (image_header_.GetImageSection(section).Contains(offset)) {
+ ++private_dirty_pages_for_section[i];
+ }
+ }
}
}
}
@@ -1138,7 +1146,19 @@ class ImgDiagDumper {
<< mapping_data->dirty_pages << " pages are dirty;\n "
<< mapping_data->false_dirty_pages << " pages are false dirty;\n "
<< mapping_data->private_pages << " pages are private;\n "
- << mapping_data->private_dirty_pages << " pages are Private_Dirty\n ";
+ << mapping_data->private_dirty_pages << " pages are Private_Dirty\n "
+ << "\n";
+
+ size_t total_private_dirty_pages = std::accumulate(private_dirty_pages_for_section.begin(),
+ private_dirty_pages_for_section.end(),
+ 0u);
+ os << "Image sections (total private dirty pages " << total_private_dirty_pages << ")\n";
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
+ os << section << " " << image_header_.GetImageSection(section)
+ << " private dirty pages=" << private_dirty_pages_for_section[i] << "\n";
+ }
+ os << "\n";
return true;
}
diff --git a/runtime/openjdkjvm/Android.bp b/openjdkjvm/Android.bp
index 37112b61e7..071b4348f8 100644
--- a/runtime/openjdkjvm/Android.bp
+++ b/openjdkjvm/Android.bp
@@ -18,7 +18,6 @@ cc_defaults {
defaults: ["art_defaults"],
host_supported: true,
srcs: ["OpenjdkJvm.cc"],
- include_dirs: ["art/runtime"],
shared_libs: [
"libbase",
"libnativehelper"
diff --git a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
index e69de29bb2..e69de29bb2 100644
--- a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
+++ b/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
diff --git a/runtime/openjdkjvm/NOTICE b/openjdkjvm/NOTICE
index 700a206a6c..700a206a6c 100644
--- a/runtime/openjdkjvm/NOTICE
+++ b/openjdkjvm/NOTICE
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index b212ea1c20..b212ea1c20 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 8d15c349dd..952f8bf0c3 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -25,6 +25,7 @@ cc_defaults {
defaults: ["art_defaults"],
host_supported: true,
srcs: [
+ "aot_class_linker.cc",
"art_field.cc",
"art_method.cc",
"atomic.cc",
@@ -50,7 +51,6 @@ cc_defaults {
"class_linker.cc",
"class_loader_context.cc",
"class_table.cc",
- "code_simulator_container.cc",
"common_throws.cc",
"compiler_filter.cc",
"debugger.cc",
@@ -627,7 +627,5 @@ art_cc_test {
}
subdirs = [
- "openjdkjvm",
"openjdkjvmti",
- "simulator",
]
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
new file mode 100644
index 0000000000..871604bcaf
--- /dev/null
+++ b/runtime/aot_class_linker.cc
@@ -0,0 +1,58 @@
+/*
+ * 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 "aot_class_linker.h"
+
+#include "handle_scope-inl.h"
+#include "mirror/class.h"
+#include "mirror/object-inl.h"
+#include "runtime.h"
+
+namespace art {
+
+AotClassLinker::AotClassLinker(InternTable *intern_table) : ClassLinker(intern_table) {}
+
+AotClassLinker::~AotClassLinker() {}
+
+// Wrap the original InitializeClass with creation of transaction when in strict mode.
+bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
+ bool can_init_statics, bool can_init_parents) {
+ Runtime* const runtime = Runtime::Current();
+
+ DCHECK(klass != nullptr);
+ if (klass->IsInitialized() || klass->IsInitializing()) {
+ return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
+ }
+
+ if (runtime->IsActiveStrictTransactionMode()) {
+ runtime->EnterTransactionMode(true, klass.Get()->AsClass());
+ }
+ bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
+
+ if (runtime->IsActiveStrictTransactionMode()) {
+ if (success) {
+ // Exit Transaction if success.
+ runtime->ExitTransactionMode();
+ } else {
+ // If not successfully initialized, the last transaction must abort. Don't rollback
+ // immediately, leave the cleanup to compiler driver which needs abort message and exception.
+ DCHECK(runtime->IsTransactionAborted());
+ DCHECK(self->IsExceptionPending());
+ }
+ }
+ return success;
+}
+} // namespace art
diff --git a/runtime/aot_class_linker.h b/runtime/aot_class_linker.h
new file mode 100644
index 0000000000..11bea86fc4
--- /dev/null
+++ b/runtime/aot_class_linker.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_AOT_CLASS_LINKER_H_
+#define ART_RUNTIME_AOT_CLASS_LINKER_H_
+
+#include "class_linker.h"
+
+namespace art {
+// AotClassLinker is only used for AOT compiler, which includes some logic for class initialization
+// which will only be used in pre-compilation.
+class AotClassLinker : public ClassLinker {
+ public:
+ explicit AotClassLinker(InternTable *intern_table);
+ ~AotClassLinker();
+
+ bool InitializeClass(Thread *self,
+ Handle<mirror::Class> klass,
+ bool can_run_clinit,
+ bool can_init_parents)
+ OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+};
+} // namespace art
+
+#endif // ART_RUNTIME_AOT_CLASS_LINKER_H_
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9a9f125718..fad927804e 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -154,20 +154,22 @@ inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType
return GetDexCacheResolvedMethods(pointer_size) == other_cache;
}
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
- // TODO: Refactor this function into two functions, Resolve...() and Lookup...()
- // so that we can properly annotate it with no-suspension possible / suspension possible.
+inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) {
ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
if (UNLIKELY(type == nullptr)) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (resolve) {
- type = class_linker->ResolveType(type_idx, this);
- CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
- } else {
- type = class_linker->LookupResolvedType(
- *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
- }
+ type = Runtime::Current()->GetClassLinker()->LookupResolvedType(
+ *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
+ }
+ return type.Ptr();
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::ResolveClassFromTypeIndex(dex::TypeIndex type_idx) {
+ ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
+ ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+ if (UNLIKELY(type == nullptr)) {
+ type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+ CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
}
return type.Ptr();
}
@@ -294,7 +296,7 @@ inline const DexFile::CodeItem* ArtMethod::GetCodeItem() {
inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) {
DCHECK(!IsProxyMethod());
- return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr;
+ return LookupResolvedClassFromTypeIndex(type_idx) != nullptr;
}
inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -403,13 +405,20 @@ inline void ArtMethod::SetDexCacheResolvedMethods(mirror::MethodDexCacheType* ne
pointer_size);
}
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
+inline dex::TypeIndex ArtMethod::GetReturnTypeIndex() {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
- dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
- return GetClassFromTypeIndex(return_type_idx, resolve);
+ return proto_id.return_type_idx_;
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedReturnType() {
+ return LookupResolvedClassFromTypeIndex(GetReturnTypeIndex());
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::ResolveReturnType() {
+ return ResolveClassFromTypeIndex(GetReturnTypeIndex());
}
inline bool ArtMethod::HasSingleImplementation() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 631f5e7250..7d8dedab6f 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -280,7 +280,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type,
break;
}
// Does this catch exception type apply?
- mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */);
+ ObjPtr<mirror::Class> iter_exception_type = ResolveClassFromTypeIndex(iter_type_idx);
if (UNLIKELY(iter_exception_type == nullptr)) {
// Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
// removed by a pro-guard like tool.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 511ac8359c..cac40e011a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -376,8 +376,11 @@ class ArtMethod FINAL {
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Get the Class* from the type index into this method's dex cache.
- mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve)
+ // Lookup the Class* from the type index into this method's dex cache.
+ ObjPtr<mirror::Class> LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Resolve the Class* from the type index into this method's dex cache.
+ ObjPtr<mirror::Class> ResolveClassFromTypeIndex(dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if this method has the same name and signature of the other method.
@@ -592,9 +595,11 @@ class ArtMethod FINAL {
const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
- // number of bugs at call sites.
- mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Lookup return type.
+ ObjPtr<mirror::Class> LookupResolvedReturnType() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Resolve return type. May cause thread suspension due to GetClassFromTypeIdx
+ // calling ResolveType this caused a large number of bugs at call sites.
+ ObjPtr<mirror::Class> ResolveReturnType() REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -748,6 +753,8 @@ class ArtMethod FINAL {
// Compare given pointer size to the image pointer size.
static bool IsImagePointerSize(PointerSize pointer_size);
+ dex::TypeIndex GetReturnTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_);
+
template<typename T>
ALWAYS_INLINE T GetNativePointer(MemberOffset offset, PointerSize pointer_size) const {
static_assert(std::is_pointer<T>::value, "T must be a pointer type");
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 0096c37b33..439ecaf28e 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -312,6 +312,17 @@ inline mirror::Class* ClassLinker::GetClassRoot(ClassRoot class_root) {
return klass.Ptr();
}
+template <class Visitor>
+inline void ClassLinker::VisitClassTables(const Visitor& visitor) {
+ Thread* const self = Thread::Current();
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ for (const ClassLoaderData& data : class_loaders_) {
+ if (data.class_table != nullptr) {
+ visitor(data.class_table);
+ }
+ }
+}
+
} // namespace art
#endif // ART_RUNTIME_CLASS_LINKER_INL_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 74c04d19b6..1219f6f39f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -370,7 +370,10 @@ ClassLinker::ClassLinker(InternTable* intern_table)
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
quick_to_interpreter_bridge_trampoline_(nullptr),
- image_pointer_size_(kRuntimePointerSize) {
+ image_pointer_size_(kRuntimePointerSize),
+ cha_(Runtime::Current()->IsAotCompiler() ? nullptr : new ClassHierarchyAnalysis()) {
+ // For CHA disabled during Aot, see b/34193647.
+
CHECK(intern_table_ != nullptr);
static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_),
"Array cache size wrong.");
@@ -1138,49 +1141,6 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
const ImageHeader& header_;
};
-class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
- public:
- explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
-
- virtual void Visit(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
- ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
- if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
- CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass);
- }
- }
-
- private:
- ClassTable* const table_;
-};
-
-class VerifyDirectInterfacesInTableClassVisitor {
- public:
- explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader)
- : class_loader_(class_loader), self_(Thread::Current()) { }
-
- bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) {
- classes_.push_back(klass);
- }
- return true;
- }
-
- void Check() const REQUIRES_SHARED(Locks::mutator_lock_) {
- for (ObjPtr<mirror::Class> klass : classes_) {
- for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
- CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr)
- << klass->PrettyDescriptor() << " iface #" << i;
- }
- }
- }
-
- private:
- ObjPtr<mirror::ClassLoader> class_loader_;
- Thread* self_;
- std::vector<ObjPtr<mirror::Class>> classes_;
-};
-
class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
public:
VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -1763,6 +1723,63 @@ class ImageSanityChecks FINAL {
std::vector<const ImageSection*> runtime_method_sections_;
};
+static void VerifyAppImage(const ImageHeader& header,
+ const Handle<mirror::ClassLoader>& class_loader,
+ const Handle<mirror::ObjectArray<mirror::DexCache> >& dex_caches,
+ ClassTable* class_table, gc::space::ImageSpace* space)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ {
+ class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
+ public:
+ explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
+
+ virtual void Visit(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
+ ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
+ if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass);
+ }
+ }
+
+ private:
+ ClassTable* const table_;
+ };
+ VerifyClassInTableArtMethodVisitor visitor(class_table);
+ header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
+ }
+ {
+ // Verify that all direct interfaces of classes in the class table are also resolved.
+ std::vector<ObjPtr<mirror::Class>> classes;
+ auto verify_direct_interfaces_in_table = [&](ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader.Get()) {
+ classes.push_back(klass);
+ }
+ return true;
+ };
+ class_table->Visit(verify_direct_interfaces_in_table);
+ Thread* self = Thread::Current();
+ for (ObjPtr<mirror::Class> klass : classes) {
+ for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
+ CHECK(klass->GetDirectInterface(self, klass, i) != nullptr)
+ << klass->PrettyDescriptor() << " iface #" << i;
+ }
+ }
+ }
+ // Check that all non-primitive classes in dex caches are also in the class table.
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+ ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+ if (klass != nullptr && !klass->IsPrimitive()) {
+ CHECK(class_table->Contains(klass))
+ << klass->PrettyDescriptor() << " " << dex_cache->GetDexFile()->GetLocation();
+ }
+ }
+ }
+}
+
bool ClassLinker::AddImageSpace(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -2016,28 +2033,13 @@ bool ClassLinker::AddImageSpace(
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
class_table->AddClassSet(std::move(temp_set));
}
+
if (kIsDebugBuild && app_image) {
// This verification needs to happen after the classes have been added to the class loader.
// Since it ensures classes are in the class table.
- VerifyClassInTableArtMethodVisitor visitor2(class_table);
- header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize);
- // Verify that all direct interfaces of classes in the class table are also resolved.
- VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get());
- class_table->Visit(visitor);
- visitor.Check();
- // Check that all non-primitive classes in dex caches are also in the class table.
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
- mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
- for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
- ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
- if (klass != nullptr && !klass->IsPrimitive()) {
- CHECK(class_table->Contains(klass)) << klass->PrettyDescriptor()
- << " " << dex_cache->GetDexFile()->GetLocation();
- }
- }
- }
+ VerifyAppImage(header, class_loader, dex_caches, class_table, space);
}
+
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
}
@@ -2315,16 +2317,15 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) {
JavaVMExt* const vm = runtime->GetJavaVM();
vm->DeleteWeakGlobalRef(self, data.weak_root);
// Notify the JIT that we need to remove the methods and/or profiling info.
- ClassHierarchyAnalysis* const cha = runtime->GetClassHierarchyAnalysis();
if (runtime->GetJit() != nullptr) {
jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
if (code_cache != nullptr) {
// For the JIT case, RemoveMethodsIn removes the CHA dependencies.
code_cache->RemoveMethodsIn(self, *data.allocator);
}
- } else {
+ } else if (cha_ != nullptr) {
// If we don't have a JIT, we need to manually remove the CHA dependencies manually.
- cha->RemoveDependenciesForLinearAlloc(data.allocator);
+ cha_->RemoveDependenciesForLinearAlloc(data.allocator);
}
delete data.allocator;
delete data.class_table;
@@ -3494,7 +3495,8 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file,
ObjPtr<mirror::DexCache> dex_cache) {
CHECK(dex_cache != nullptr) << dex_file.GetLocation();
boot_class_path_.push_back(&dex_file);
- RegisterBootClassPathDexFile(dex_file, dex_cache);
+ WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_);
+ RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr);
}
void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file,
@@ -3553,6 +3555,14 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file,
data.resolved_methods = dex_cache->GetResolvedMethods();
data.class_table = ClassTableForClassLoader(class_loader);
DCHECK(data.class_table != nullptr);
+ // Make sure to hold the dex cache live in the class table. This case happens for the boot class
+ // path dex caches without an image.
+ data.class_table->InsertStrongRoot(dex_cache);
+ if (class_loader != nullptr) {
+ // Since we added a strong root to the class table, do the write barrier as required for
+ // remembered sets and generational GCs.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+ }
dex_caches_.push_back(data);
}
@@ -3677,12 +3687,6 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file,
return h_dex_cache.Get();
}
-void ClassLinker::RegisterBootClassPathDexFile(const DexFile& dex_file,
- ObjPtr<mirror::DexCache> dex_cache) {
- WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_);
- RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr);
-}
-
bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) {
ReaderMutexLock mu(self, *Locks::dex_lock_);
return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr;
@@ -4716,7 +4720,7 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons
CHECK_STREQ(np->GetName(), prototype->GetName());
CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
// More complex sanity - via dex cache
- CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */));
+ CHECK_EQ(np->ResolveReturnType(), prototype->ResolveReturnType());
}
bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
@@ -5192,12 +5196,12 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
REQUIRES_SHARED(Locks::mutator_lock_) {
{
StackHandleScope<1> hs(self);
- Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method1->ResolveReturnType()));
if (UNLIKELY(return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
return false;
}
- ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> other_return_type = method2->ResolveReturnType();
if (UNLIKELY(other_return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
return false;
@@ -5242,7 +5246,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
StackHandleScope<1> hs(self);
dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
Handle<mirror::Class> param_type(hs.NewHandle(
- method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
+ method1->ResolveClassFromTypeIndex(param_type_idx)));
if (UNLIKELY(param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method1, i, param_type_idx);
@@ -5250,7 +5254,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
}
dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
ObjPtr<mirror::Class> other_param_type =
- method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */);
+ method2->ResolveClassFromTypeIndex(other_param_type_idx);
if (UNLIKELY(other_param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method2, i, other_param_type_idx);
@@ -5487,7 +5491,9 @@ bool ClassLinker::LinkClass(Thread* self,
// Update CHA info based on whether we override methods.
// Have to do this before setting the class as resolved which allows
// instantiation of klass.
- Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(klass);
+ if (cha_ != nullptr) {
+ cha_->UpdateAfterLoadingOf(klass);
+ }
// This will notify waiters on klass that saw the not yet resolved
// class in the class_table_ during EnsureResolved.
@@ -5535,7 +5541,9 @@ bool ClassLinker::LinkClass(Thread* self,
// Update CHA info based on whether we override methods.
// Have to do this before setting the class as resolved which allows
// instantiation of klass.
- Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(h_new_class);
+ if (cha_ != nullptr) {
+ cha_->UpdateAfterLoadingOf(h_new_class);
+ }
// This will notify waiters on temp class that saw the not yet resolved class in the
// class_table_ during EnsureResolved.
@@ -8510,7 +8518,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod(
it.Next();
}
- Handle<mirror::Class> return_type = hs.NewHandle(target_method->GetReturnType(true));
+ Handle<mirror::Class> return_type = hs.NewHandle(target_method->ResolveReturnType());
if (UNLIKELY(return_type.IsNull())) {
DCHECK(self->IsExceptionPending());
return nullptr;
@@ -8587,7 +8595,7 @@ void ClassLinker::SetEntryPointsToInterpreter(ArtMethod* method) const {
if (!method->IsNative()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else {
- SetEntryPointsToCompiledCode(method, GetQuickGenericJniStub());
+ method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
}
}
@@ -8839,16 +8847,6 @@ void ClassLinker::DropFindArrayClassCache() {
find_array_class_cache_next_victim_ = 0;
}
-void ClassLinker::ClearClassTableStrongRoots() const {
- Thread* const self = Thread::Current();
- WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- for (const ClassLoaderData& data : class_loaders_) {
- if (data.class_table != nullptr) {
- data.class_table->ClearStrongRoots();
- }
- }
-}
-
void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const {
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cb28187839..bf14aebb52 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -60,6 +60,7 @@ namespace mirror {
using MethodDexCacheType = std::atomic<MethodDexCachePair>;
} // namespace mirror
+class ClassHierarchyAnalysis;
class ClassTable;
template<class T> class Handle;
class ImtConflictTable;
@@ -140,7 +141,7 @@ class ClassLinker {
};
explicit ClassLinker(InternTable* intern_table);
- ~ClassLinker();
+ virtual ~ClassLinker();
// Initialize class linker by bootstraping from dex files.
bool InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path,
@@ -396,9 +397,6 @@ class ClassLinker {
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- void RegisterBootClassPathDexFile(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache)
- REQUIRES(!Locks::dex_lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
const std::vector<const DexFile*>& GetBootClassPath() {
return boot_class_path_;
@@ -637,9 +635,9 @@ class ClassLinker {
// Create the IMT and conflict tables for a class.
void FillIMTAndConflictTables(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
- // Clear class table strong roots (other than classes themselves). This is done by dex2oat to
- // allow pruning dex caches.
- void ClearClassTableStrongRoots() const
+ // Visit all of the class tables. This is used by dex2oat to allow pruning dex caches.
+ template <class Visitor>
+ void VisitClassTables(const Visitor& visitor)
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -672,6 +670,10 @@ class ClassLinker {
bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
+ ClassHierarchyAnalysis* GetClassHierarchyAnalysis() {
+ return cha_.get();
+ }
+
struct DexCacheData {
// Construct an invalid data object.
DexCacheData()
@@ -700,6 +702,14 @@ class ClassLinker {
ClassTable* class_table;
};
+ protected:
+ virtual bool InitializeClass(Thread* self,
+ Handle<mirror::Class> klass,
+ bool can_run_clinit,
+ bool can_init_parents)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
private:
class LinkInterfaceMethodsHelper;
@@ -718,7 +728,7 @@ class ClassLinker {
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- static void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
+ void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
REQUIRES_SHARED(Locks::mutator_lock_);
void VisitClassesInternal(ClassVisitor* visitor)
@@ -889,12 +899,6 @@ class ClassLinker {
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool InitializeClass(Thread* self,
- Handle<mirror::Class> klass,
- bool can_run_clinit,
- bool can_init_parents)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
bool InitializeDefaultInterfaceRecursive(Thread* self,
Handle<mirror::Class> klass,
bool can_run_clinit,
@@ -1268,6 +1272,8 @@ class ClassLinker {
// Image pointer size.
PointerSize image_pointer_size_;
+ std::unique_ptr<ClassHierarchyAnalysis> cha_;
+
class FindVirtualMethodHolderVisitor;
friend class AppImageClassLoadersAndDexCachesHelper;
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index eab3b86d3d..92d0f8d5ae 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -145,6 +145,10 @@ ClassLoaderContext::ExtractClassLoaderType(const std::string& class_loader_spec)
// ClasspathElem is the path of dex/jar/apk file.
bool ClassLoaderContext::Parse(const std::string& spec, bool parse_checksums) {
if (spec.empty()) {
+ // By default we load the dex files in a PathClassLoader.
+ // So an empty spec is equivalent to an empty PathClassLoader (this happens when running
+ // tests)
+ class_loader_chain_.push_back(ClassLoaderInfo(kPathClassLoader));
return true;
}
@@ -265,12 +269,16 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_
return OatFile::kSpecialSharedLibrary;
}
+ std::ostringstream out;
if (class_loader_chain_.empty()) {
- return "";
+ // We can get in this situation if the context was created with a class path containing the
+ // source dex files which were later removed (happens during run-tests).
+ out << GetClassLoaderTypeName(kPathClassLoader)
+ << kClassLoaderOpeningMark
+ << kClassLoaderClosingMark;
+ return out.str();
}
- std::ostringstream out;
-
for (size_t i = 0; i < class_loader_chain_.size(); i++) {
const ClassLoaderInfo& info = class_loader_chain_[i];
if (i > 0) {
@@ -599,7 +607,8 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex
if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) {
LOG(WARNING) << "ClassLoaderContext size mismatch. expected="
<< expected_context.class_loader_chain_.size()
- << ", actual=" << class_loader_chain_.size();
+ << ", actual=" << class_loader_chain_.size()
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
@@ -609,13 +618,15 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex
if (info.type != expected_info.type) {
LOG(WARNING) << "ClassLoaderContext type mismatch for position " << i
<< ". expected=" << GetClassLoaderTypeName(expected_info.type)
- << ", found=" << GetClassLoaderTypeName(info.type);
+ << ", found=" << GetClassLoaderTypeName(info.type)
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
if (info.classpath.size() != expected_info.classpath.size()) {
LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i
<< ". expected=" << expected_info.classpath.size()
- << ", found=" << info.classpath.size();
+ << ", found=" << info.classpath.size()
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
@@ -626,13 +637,15 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex
if (info.classpath[k] != expected_info.classpath[k]) {
LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
<< ". expected=" << expected_info.classpath[k]
- << ", found=" << info.classpath[k];
+ << ", found=" << info.classpath[k]
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
if (info.checksums[k] != expected_info.checksums[k]) {
LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
<< ". expected=" << expected_info.checksums[k]
- << ", found=" << info.checksums[k];
+ << ", found=" << info.checksums[k]
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
}
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 5655aecbe5..2b85188e38 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -161,6 +161,19 @@ class ClassLoaderContextTest : public CommonRuntimeTest {
}
};
+TEST_F(ClassLoaderContextTest, ParseValidEmptyContext) {
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("");
+ // An empty context should create a single empty PathClassLoader.
+ VerifyContextSize(context.get(), 1);
+ VerifyClassLoaderPCL(context.get(), 0, "");
+}
+
+TEST_F(ClassLoaderContextTest, ParseValidSharedLibraryContext) {
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("&");
+ // An shared library context should have no class loader in the chain.
+ VerifyContextSize(context.get(), 0);
+}
+
TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[a.dex]");
@@ -312,6 +325,34 @@ TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) {
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
}
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraryContext) {
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("&");
+
+ ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+ std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
+
+ std::vector<const DexFile*> compilation_sources_raw =
+ MakeNonOwningPointerVector(compilation_sources);
+ jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
+ ASSERT_TRUE(jclass_loader != nullptr);
+
+ ScopedObjectAccess soa(Thread::Current());
+
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
+ soa.Decode<mirror::ClassLoader>(jclass_loader));
+
+ // A shared library context should create a single PathClassLoader with only the compilation
+ // sources.
+ VerifyClassLoaderDexFiles(soa,
+ class_loader,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ compilation_sources_raw);
+ ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
+ soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
+
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) {
// Setup the context.
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index b15d82f5e4..1280466a91 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -132,6 +132,13 @@ inline ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass, uint32_t de
}
}
+template <typename Filter>
+inline void ClassTable::RemoveStrongRoots(const Filter& filter) {
+ WriterMutexLock mu(Thread::Current(), lock_);
+ strong_roots_.erase(std::remove_if(strong_roots_.begin(), strong_roots_.end(), filter),
+ strong_roots_.end());
+}
+
} // namespace art
#endif // ART_RUNTIME_CLASS_TABLE_INL_H_
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 8616dfba93..a259725399 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -250,6 +250,12 @@ class ClassTable {
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Filter strong roots (other than classes themselves).
+ template <typename Filter>
+ void RemoveStrongRoots(const Filter& filter)
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ReaderWriterMutex& GetLock() {
return lock_;
}
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 5a87ae8420..0b7af4e856 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4008,8 +4008,8 @@ JDWP::JdwpError Dbg::PrepareInvokeMethod(uint32_t request_id, JDWP::ObjectId thr
if (shorty[i + 1] == 'L') {
// Did we really get an argument of an appropriate reference type?
- mirror::Class* parameter_type =
- m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */);
+ ObjPtr<mirror::Class> parameter_type =
+ m->ResolveClassFromTypeIndex(types->GetTypeItem(i).type_idx_);
mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 2b81f0a99a..4225ab9b6e 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -695,8 +695,7 @@ mirror::Object* CreateAnnotationMember(const ClassData& klass,
if (annotation_method == nullptr) {
return nullptr;
}
- Handle<mirror::Class> method_return(hs.NewHandle(
- annotation_method->GetReturnType(true /* resolve */)));
+ Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->ResolveReturnType()));
DexFile::AnnotationValue annotation_value;
if (!ProcessAnnotationValue<false>(klass,
@@ -762,15 +761,16 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet(
}
const uint8_t* annotation = annotation_item->annotation_;
uint32_t type_index = DecodeUnsignedLeb128(&annotation);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
mirror::Class* resolved_class;
if (lookup_in_resolved_boot_classes) {
+ // Note: We cannot use ClassLinker::LookupResolvedType() because the current DexCache
+ // may not be registered with the boot class path ClassLoader and we must not pollute
+ // the DexCache with classes that are not in the associated ClassLoader's ClassTable.
+ const char* descriptor = dex_file.StringByTypeIdx(dex::TypeIndex(type_index));
ObjPtr<mirror::Class> looked_up_class =
- Runtime::Current()->GetClassLinker()->LookupResolvedType(
- klass.GetDexFile(),
- dex::TypeIndex(type_index),
- klass.GetDexCache(),
- // Force the use of the bootstrap class loader.
- static_cast<mirror::ClassLoader*>(nullptr));
+ class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr);
resolved_class = looked_up_class.Ptr();
if (resolved_class == nullptr) {
// If `resolved_class` is null, this is fine: just ignore that
@@ -779,8 +779,8 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet(
continue;
}
} else {
- StackHandleScope<2> hs(Thread::Current());
- resolved_class = Runtime::Current()->GetClassLinker()->ResolveType(
+ StackHandleScope<2> hs(self);
+ resolved_class = class_linker->ResolveType(
klass.GetDexFile(),
dex::TypeIndex(type_index),
hs.NewHandle(klass.GetDexCache()),
@@ -789,8 +789,8 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet(
std::string temp;
LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d",
klass.GetRealClass()->GetDescriptor(&temp), type_index);
- CHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
continue;
}
}
@@ -1073,7 +1073,7 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) {
}
DexFile::AnnotationValue annotation_value;
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method->ResolveReturnType()));
if (!ProcessAnnotationValue<false>(klass,
&annotation,
&annotation_value,
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 01fc9ce668..2bf4372b1f 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -45,7 +45,7 @@ void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
}
// Make sure that the result is an instance of the type this method was expected to return.
ArtMethod* method = self->GetCurrentMethod(nullptr);
- mirror::Class* return_type = method->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> return_type = method->ResolveReturnType();
if (!o->InstanceOf(return_type)) {
Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -108,7 +108,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons
ArtMethod* interface_method =
soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
// This can cause thread suspension.
- mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> result_type = interface_method->ResolveReturnType();
ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
JValue result_unboxed;
if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 90b5def9fe..9969489648 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -196,6 +196,10 @@ class Instrumentation {
}
bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool CanDeoptimize() {
+ return deoptimization_enabled_;
+ }
+
// Executes everything with interpreter.
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
@@ -457,8 +461,7 @@ class Instrumentation {
// This is used by the debugger to cause a deoptimization of the thread's stack after updating
// local variable(s).
void InstrumentThreadStack(Thread* thread)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::thread_list_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
static size_t ComputeFrameId(Thread* self,
size_t frame_depth,
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 85904ee4ed..136d0c6b64 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1070,7 +1070,7 @@ static inline bool DoCallCommon(ArtMethod* called_method,
// Preserve o since it is used below and GetClassFromTypeIndex may cause thread
// suspension.
HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
- arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */);
+ arg_type = method->ResolveClassFromTypeIndex(type_idx);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
return false;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 0a2705d5f7..bdb83326fd 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -349,7 +349,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
const size_t ref_idx = inst->VRegA_11x(inst_data);
ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
- ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> return_type = method->ResolveReturnType();
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 2aa2ff2ef2..a030a51473 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -642,7 +642,7 @@ void JitCodeCache::FreeAllMethodHeaders(
// method_headers are expected to be in the executable region.
{
MutexLock mu(Thread::Current(), *Locks::cha_lock_);
- Runtime::Current()->GetClassHierarchyAnalysis()
+ Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()
->RemoveDependentsWithMethodHeaders(method_headers);
}
@@ -977,7 +977,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
<< "Should not be using cha on debuggable apps/runs!";
for (ArtMethod* single_impl : cha_single_implementation_list) {
- Runtime::Current()->GetClassHierarchyAnalysis()->AddDependency(
+ Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()->AddDependency(
single_impl, method, method_header);
}
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 2aad12d3b8..f209f1d73a 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -260,7 +260,7 @@ static jobject Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaM
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
- ObjPtr<mirror::Class> return_type(method->GetReturnType(true /* resolve */));
+ ObjPtr<mirror::Class> return_type(method->ResolveReturnType());
if (return_type.IsNull()) {
CHECK(soa.Self()->IsExceptionPending());
return nullptr;
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 3c1311b18a..af770724ab 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -503,112 +503,112 @@ class JvmtiFunctions {
}
static jvmtiError GetLocalObject(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jobject* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalInstance(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jobject* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jobject* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalInstance(env, thread, depth, value_ptr);
}
static jvmtiError GetLocalInt(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jint* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalLong(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jlong* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalFloat(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jfloat* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalDouble(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jdouble* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError SetLocalObject(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jobject value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalInt(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jint value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalLong(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jlong value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalFloat(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jfloat value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalDouble(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jdouble value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
@@ -904,12 +904,12 @@ class JvmtiFunctions {
}
static jvmtiError GetLocalVariableTable(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jint* entry_count_ptr ATTRIBUTE_UNUSED,
- jvmtiLocalVariableEntry** table_ptr ATTRIBUTE_UNUSED) {
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariableTable(env, method, entry_count_ptr, table_ptr);
}
static jvmtiError GetBytecodes(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 4d5bb95f2c..ce30c244dc 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -204,6 +204,10 @@ static inline jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
ALWAYS_INLINE
static inline JvmtiUniquePtr<char[]> CopyString(jvmtiEnv* env, const char* src, jvmtiError* error) {
+ if (src == nullptr) {
+ JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, 0, error);
+ return ret;
+ }
size_t len = strlen(src) + 1;
JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, len, error);
if (ret != nullptr) {
@@ -227,8 +231,8 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_source_file_name = 1,
.can_get_line_numbers = 1,
.can_get_source_debug_extension = 1,
- .can_access_local_variables = 0,
- .can_maintain_original_method_order = 0,
+ .can_access_local_variables = 1,
+ .can_maintain_original_method_order = 1,
.can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
.can_generate_frame_pop_events = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 43177ab845..91e40553f0 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -414,9 +414,10 @@ inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
bool added) {
ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
: ArtJvmtiEvent::kClassFileLoadHookRetransformable;
- return caps.can_retransform_classes == 1 &&
- IsEventEnabledAnywhere(event) &&
- env->event_masks.IsEnabledAnywhere(event);
+ return (added && caps.can_access_local_variables == 1) ||
+ (caps.can_retransform_classes == 1 &&
+ IsEventEnabledAnywhere(event) &&
+ env->event_masks.IsEnabledAnywhere(event));
}
inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
@@ -428,6 +429,9 @@ inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
}
+ if (added && caps.can_access_local_variables == 1) {
+ HandleLocalAccessCapabilityAdded();
+ }
}
}
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 7a930d4163..2944a453e0 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -610,6 +610,21 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener,
}
}
+void EventHandler::HandleLocalAccessCapabilityAdded() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative);
+ art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation();
+ art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(),
+ art::gc::kGcCauseInstrumentation,
+ art::gc::kCollectorTypeInstrumentation);
+ art::ScopedSuspendAll ssa("Deoptimize everything for local variable access", true);
+ // TODO This should be disabled when there are no environments using it.
+ if (!instr->CanDeoptimize()) {
+ instr->EnableDeoptimization();
+ }
+ // TODO We should be able to support can_access_local_variables without this.
+ instr->DeoptimizeEverything("jvmti-local-variable-access");
+}
+
// Handle special work for the given event type, if necessary.
void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
switch (event) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 5f37dcf0a7..617519eaa3 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -210,6 +210,7 @@ class EventHandler {
unsigned char** new_class_data) const;
void HandleEventType(ArtJvmtiEvent event, bool enable);
+ void HandleLocalAccessCapabilityAdded();
// List of all JvmTiEnv objects that have been created, in their creation order.
// NB Some elements might be null representing envs that have been deleted. They should be skipped
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index ab434d7d9a..8f727147a4 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -34,16 +34,22 @@
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/mutex-inl.h"
#include "dex_file_annotations.h"
#include "events-inl.h"
#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "modifiers.h"
#include "nativehelper/ScopedLocalRef.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_thread.h"
#include "ti_phase.h"
namespace openjdkjvmti {
@@ -159,6 +165,106 @@ jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
return ERR(NONE);
}
+jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (entry_count_ptr == nullptr || table_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ const art::DexFile* dex_file = art_method->GetDexFile();
+ const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+ // TODO code_item == nullptr means that the method is abstract (or native, but we check that
+ // earlier). We should check what is returned by the RI in this situation since it's not clear
+ // what the appropriate return value is from the spec.
+ if (dex_file == nullptr || code_item == nullptr) {
+ return ERR(ABSENT_INFORMATION);
+ }
+
+ struct LocalVariableContext {
+ explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {}
+
+ static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
+ reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry);
+ }
+
+ void Insert(const art::DexFile::LocalInfo& entry) {
+ if (err_ != OK) {
+ return;
+ }
+ JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_);
+ if (err_ != OK) {
+ return;
+ }
+ JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_);
+ if (err_ != OK) {
+ return;
+ }
+ JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_);
+ if (err_ != OK) {
+ return;
+ }
+ variables_.push_back({
+ .start_location = static_cast<jlocation>(entry.start_address_),
+ .length = static_cast<jint>(entry.end_address_ - entry.start_address_),
+ .name = name_str.release(),
+ .signature = sig_str.release(),
+ .generic_signature = generic_sig_str.release(),
+ .slot = entry.reg_,
+ });
+ }
+
+ jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) {
+ jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size();
+ if (err_ != OK ||
+ (err_ = env_->Allocate(table_size,
+ reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) {
+ Cleanup();
+ return err_;
+ } else {
+ *out_entry_count_ptr = variables_.size();
+ memcpy(*out_table_ptr, variables_.data(), table_size);
+ return OK;
+ }
+ }
+
+ void Cleanup() {
+ for (jvmtiLocalVariableEntry& e : variables_) {
+ env_->Deallocate(reinterpret_cast<unsigned char*>(e.name));
+ env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature));
+ env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature));
+ }
+ }
+
+ jvmtiEnv* env_;
+ std::vector<jvmtiLocalVariableEntry> variables_;
+ jvmtiError err_;
+ };
+
+ LocalVariableContext context(env);
+ if (!dex_file->DecodeDebugLocalInfo(code_item,
+ art_method->IsStatic(),
+ art_method->GetDexMethodIndex(),
+ LocalVariableContext::Callback,
+ &context)) {
+ // Something went wrong with decoding the debug information. It might as well not be there.
+ return ERR(ABSENT_INFORMATION);
+ } else {
+ return context.Release(entry_count_ptr, table_ptr);
+ }
+}
+
jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
jmethodID method,
jint* max_ptr) {
@@ -425,4 +531,541 @@ jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* i
return IsMethodT(env, m, test, is_synthetic_ptr);
}
+struct FindFrameAtDepthVisitor : art::StackVisitor {
+ public:
+ FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ found_frame_(false),
+ cnt_(0),
+ depth_(static_cast<size_t>(depth)) { }
+
+ bool FoundFrame() {
+ return found_frame_;
+ }
+
+ bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+ if (GetMethod()->IsRuntimeMethod()) {
+ return true;
+ }
+ if (cnt_ == depth_) {
+ // We found our frame, exit.
+ found_frame_ = true;
+ return false;
+ } else {
+ cnt_++;
+ return true;
+ }
+ }
+
+ private:
+ bool found_frame_;
+ size_t cnt_;
+ size_t depth_;
+};
+
+class CommonLocalVariableClosure : public art::Closure {
+ public:
+ CommonLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot)
+ : result_(ERR(INTERNAL)), caller_(caller), depth_(depth), slot_(slot) {}
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ // Must have been a bad depth.
+ result_ = ERR(NO_MORE_FRAMES);
+ return;
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (method->IsNative() || !visitor.IsShadowFrame()) {
+ // TODO We really should support get/set for non-shadow frames.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ } else if (method->GetCodeItem()->registers_size_ <= slot_) {
+ result_ = ERR(INVALID_SLOT);
+ return;
+ }
+ uint32_t pc = visitor.GetDexPc(/*abort_on_failure*/ false);
+ if (pc == art::DexFile::kDexNoIndex) {
+ // Cannot figure out current PC.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ }
+ std::string descriptor;
+ art::Primitive::Type slot_type = art::Primitive::kPrimVoid;
+ jvmtiError err = GetSlotType(method, pc, &descriptor, &slot_type);
+ if (err != OK) {
+ result_ = err;
+ return;
+ }
+
+ err = GetTypeError(method, slot_type, descriptor);
+ if (err != OK) {
+ result_ = err;
+ return;
+ }
+ result_ = Execute(method, visitor);
+ }
+
+ jvmtiError GetResult() const {
+ return result_;
+ }
+
+ protected:
+ virtual jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ REQUIRES(art::Locks::mutator_lock_) = 0;
+ virtual jvmtiError GetTypeError(art::ArtMethod* method,
+ art::Primitive::Type type,
+ const std::string& descriptor)
+ REQUIRES(art::Locks::mutator_lock_) = 0;
+
+ jvmtiError GetSlotType(art::ArtMethod* method,
+ uint32_t dex_pc,
+ /*out*/std::string* descriptor,
+ /*out*/art::Primitive::Type* type)
+ REQUIRES(art::Locks::mutator_lock_) {
+ const art::DexFile* dex_file = method->GetDexFile();
+ const art::DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (dex_file == nullptr || code_item == nullptr) {
+ return ERR(OPAQUE_FRAME);
+ }
+
+ struct GetLocalVariableInfoContext {
+ explicit GetLocalVariableInfoContext(jint slot,
+ uint32_t pc,
+ std::string* out_descriptor,
+ art::Primitive::Type* out_type)
+ : found_(false), jslot_(slot), pc_(pc), descriptor_(out_descriptor), type_(out_type) {
+ *descriptor_ = "";
+ *type_ = art::Primitive::kPrimVoid;
+ }
+
+ static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
+ reinterpret_cast<GetLocalVariableInfoContext*>(raw_ctx)->Handle(entry);
+ }
+
+ void Handle(const art::DexFile::LocalInfo& entry) {
+ if (found_) {
+ return;
+ } else if (entry.start_address_ <= pc_ &&
+ entry.end_address_ > pc_ &&
+ entry.reg_ == jslot_) {
+ found_ = true;
+ *type_ = art::Primitive::GetType(entry.descriptor_[0]);
+ *descriptor_ = entry.descriptor_;
+ }
+ return;
+ }
+
+ bool found_;
+ jint jslot_;
+ uint32_t pc_;
+ std::string* descriptor_;
+ art::Primitive::Type* type_;
+ };
+
+ GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
+ if (!dex_file->DecodeDebugLocalInfo(code_item,
+ method->IsStatic(),
+ method->GetDexMethodIndex(),
+ GetLocalVariableInfoContext::Callback,
+ &context) || !context.found_) {
+ // Something went wrong with decoding the debug information. It might as well not be there.
+ return ERR(INVALID_SLOT);
+ } else {
+ return OK;
+ }
+ }
+
+ jvmtiError result_;
+ art::Thread* caller_;
+ jint depth_;
+ jint slot_;
+};
+
+class GetLocalVariableClosure : public CommonLocalVariableClosure {
+ public:
+ GetLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* val)
+ : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
+
+ protected:
+ jvmtiError GetTypeError(art::ArtMethod* method ATTRIBUTE_UNUSED,
+ art::Primitive::Type slot_type,
+ const std::string& descriptor ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (slot_type) {
+ case art::Primitive::kPrimByte:
+ case art::Primitive::kPrimChar:
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimShort:
+ case art::Primitive::kPrimBoolean:
+ return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimLong:
+ case art::Primitive::kPrimFloat:
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimNot:
+ return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected primitive type " << slot_type;
+ UNREACHABLE();
+ }
+ }
+
+ jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (type_) {
+ case art::Primitive::kPrimNot: {
+ uint32_t ptr_val;
+ if (!visitor.GetVReg(method,
+ static_cast<uint16_t>(slot_),
+ art::kReferenceVReg,
+ &ptr_val)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ art::ObjPtr<art::mirror::Object> obj(reinterpret_cast<art::mirror::Object*>(ptr_val));
+ val_->l = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
+ break;
+ }
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimFloat: {
+ if (!visitor.GetVReg(method,
+ static_cast<uint16_t>(slot_),
+ type_ == art::Primitive::kPrimFloat ? art::kFloatVReg : art::kIntVReg,
+ reinterpret_cast<uint32_t*>(&val_->i))) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimLong: {
+ auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
+ auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
+ if (!visitor.GetVRegPair(method,
+ static_cast<uint16_t>(slot_),
+ lo_type,
+ high_type,
+ reinterpret_cast<uint64_t*>(&val_->j))) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "unexpected register type " << type_;
+ UNREACHABLE();
+ }
+ }
+ return OK;
+ }
+
+ private:
+ art::Primitive::Type type_;
+ jvalue* val_;
+};
+
+jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* val) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ GetLocalVariableClosure c(self, depth, slot, type, val);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+class SetLocalVariableClosure : public CommonLocalVariableClosure {
+ public:
+ SetLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue val)
+ : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
+
+ protected:
+ jvmtiError GetTypeError(art::ArtMethod* method,
+ art::Primitive::Type slot_type,
+ const std::string& descriptor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (slot_type) {
+ case art::Primitive::kPrimNot: {
+ if (type_ != art::Primitive::kPrimNot) {
+ return ERR(TYPE_MISMATCH);
+ } else if (val_.l == nullptr) {
+ return OK;
+ } else {
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ art::ObjPtr<art::mirror::Class> set_class =
+ caller_->DecodeJObject(val_.l)->GetClass();
+ art::ObjPtr<art::mirror::ClassLoader> loader =
+ method->GetDeclaringClass()->GetClassLoader();
+ art::ObjPtr<art::mirror::Class> slot_class =
+ cl->LookupClass(caller_, descriptor.c_str(), loader);
+ DCHECK(!slot_class.IsNull());
+ return slot_class->IsAssignableFrom(set_class) ? OK : ERR(TYPE_MISMATCH);
+ }
+ }
+ case art::Primitive::kPrimByte:
+ case art::Primitive::kPrimChar:
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimShort:
+ case art::Primitive::kPrimBoolean:
+ return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimLong:
+ case art::Primitive::kPrimFloat:
+ case art::Primitive::kPrimDouble:
+ return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected primitive type " << slot_type;
+ UNREACHABLE();
+ }
+ }
+
+ jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (type_) {
+ case art::Primitive::kPrimNot: {
+ uint32_t ptr_val;
+ art::ObjPtr<art::mirror::Object> obj(caller_->DecodeJObject(val_.l));
+ ptr_val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(obj.Ptr()));
+ if (!visitor.SetVReg(method,
+ static_cast<uint16_t>(slot_),
+ ptr_val,
+ art::kReferenceVReg)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimFloat: {
+ if (!visitor.SetVReg(method,
+ static_cast<uint16_t>(slot_),
+ static_cast<uint32_t>(val_.i),
+ type_ == art::Primitive::kPrimFloat ? art::kFloatVReg
+ : art::kIntVReg)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimLong: {
+ auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
+ auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
+ if (!visitor.SetVRegPair(method,
+ static_cast<uint16_t>(slot_),
+ static_cast<uint64_t>(val_.j),
+ lo_type,
+ high_type)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "unexpected register type " << type_;
+ UNREACHABLE();
+ }
+ }
+ return OK;
+ }
+
+ private:
+ art::Primitive::Type type_;
+ jvalue val_;
+};
+
+jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue val) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ SetLocalVariableClosure c(self, depth, slot, type, val);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+class GetLocalInstanceClosure : public art::Closure {
+ public:
+ GetLocalInstanceClosure(art::Thread* caller, jint depth, jobject* val)
+ : result_(ERR(INTERNAL)),
+ caller_(caller),
+ depth_(depth),
+ val_(val) {}
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ // Must have been a bad depth.
+ result_ = ERR(NO_MORE_FRAMES);
+ return;
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (!visitor.IsShadowFrame() && !method->IsNative() && !method->IsProxyMethod()) {
+ // TODO We really should support get/set for non-shadow frames.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ }
+ result_ = OK;
+ art::ObjPtr<art::mirror::Object> obj = visitor.GetThisObject();
+ *val_ = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
+ }
+
+ jvmtiError GetResult() const {
+ return result_;
+ }
+
+ private:
+ jvmtiError result_;
+ art::Thread* caller_;
+ jint depth_;
+ jobject* val_;
+};
+
+jvmtiError MethodUtil::GetLocalInstance(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jobject* data) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ GetLocalInstanceClosure c(self, depth, data);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+#define FOR_JVMTI_JVALUE_TYPES(fn) \
+ fn(jint, art::Primitive::kPrimInt, i) \
+ fn(jlong, art::Primitive::kPrimLong, j) \
+ fn(jfloat, art::Primitive::kPrimFloat, f) \
+ fn(jdouble, art::Primitive::kPrimDouble, d) \
+ fn(jobject, art::Primitive::kPrimNot, l)
+
+namespace impl {
+
+template<typename T> void WriteJvalue(T, jvalue*);
+template<typename T> void ReadJvalue(jvalue, T*);
+template<typename T> art::Primitive::Type GetJNIType();
+
+#define JNI_TYPE_CHAR(type, prim, id) \
+template<> art::Primitive::Type GetJNIType<type>() { \
+ return prim; \
+}
+
+FOR_JVMTI_JVALUE_TYPES(JNI_TYPE_CHAR);
+
+#undef JNI_TYPE_CHAR
+
+#define RW_JVALUE(type, prim, id) \
+ template<> void ReadJvalue<type>(jvalue in, type* out) { \
+ *out = in.id; \
+ } \
+ template<> void WriteJvalue<type>(type in, jvalue* out) { \
+ out->id = in; \
+ }
+
+FOR_JVMTI_JVALUE_TYPES(RW_JVALUE);
+
+#undef RW_JVALUE
+
+} // namespace impl
+
+template<typename T>
+jvmtiError MethodUtil::SetLocalVariable(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ T data) {
+ jvalue v = {.j = 0};
+ art::Primitive::Type type = impl::GetJNIType<T>();
+ impl::WriteJvalue(data, &v);
+ return SetLocalVariableGeneric(env, thread, depth, slot, type, v);
+}
+
+template<typename T>
+jvmtiError MethodUtil::GetLocalVariable(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ T* data) {
+ if (data == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ jvalue v = {.j = 0};
+ art::Primitive::Type type = impl::GetJNIType<T>();
+ jvmtiError err = GetLocalVariableGeneric(env, thread, depth, slot, type, &v);
+ if (err != OK) {
+ return err;
+ } else {
+ impl::ReadJvalue(v, data);
+ return OK;
+ }
+}
+
+#define GET_SET_LV(type, prim, id) \
+ template jvmtiError MethodUtil::GetLocalVariable<type>(jvmtiEnv*, jthread, jint, jint, type*); \
+ template jvmtiError MethodUtil::SetLocalVariable<type>(jvmtiEnv*, jthread, jint, jint, type);
+
+FOR_JVMTI_JVALUE_TYPES(GET_SET_LV);
+
+#undef GET_SET_LV
+
+#undef FOR_JVMTI_JVALUE_TYPES
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index d95a81b63b..aabaedb932 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -34,6 +34,7 @@
#include "jni.h"
#include "jvmti.h"
+#include "primitive.h"
namespace openjdkjvmti {
@@ -80,6 +81,32 @@ class MethodUtil {
static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr);
static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr);
static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr);
+ static jvmtiError GetLocalVariableTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr);
+
+ template<typename T>
+ static jvmtiError SetLocalVariable(jvmtiEnv* env, jthread thread, jint depth, jint slot, T data);
+
+ template<typename T>
+ static jvmtiError GetLocalVariable(jvmtiEnv* env, jthread thread, jint depth, jint slot, T* data);
+
+ static jvmtiError GetLocalInstance(jvmtiEnv* env, jthread thread, jint depth, jobject* data);
+
+ private:
+ static jvmtiError SetLocalVariableGeneric(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue value);
+ static jvmtiError GetLocalVariableGeneric(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* value);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 9acea2a288..6fa73f8a8c 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -159,26 +159,13 @@ jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread*
return ERR(NONE);
}
-static art::Thread* GetNativeThreadLocked(jthread thread,
- const art::ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(art::Locks::thread_list_lock_) {
- if (thread == nullptr) {
- return art::Thread::Current();
- }
-
- return art::Thread::FromManagedThread(soa, thread);
-}
-
// Get the native thread. The spec says a null object denotes the current thread.
-static art::Thread* GetNativeThread(jthread thread,
- const art::ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
+art::Thread* ThreadUtil::GetNativeThread(jthread thread,
+ const art::ScopedObjectAccessAlreadyRunnable& soa) {
if (thread == nullptr) {
return art::Thread::Current();
}
- art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
return art::Thread::FromManagedThread(soa, thread);
}
@@ -190,18 +177,20 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI
return JVMTI_ERROR_WRONG_PHASE;
}
- art::ScopedObjectAccess soa(art::Thread::Current());
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
- art::Thread* self = GetNativeThread(thread, soa);
- if (self == nullptr && thread == nullptr) {
+ art::Thread* target = GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
return ERR(INVALID_THREAD);
}
JvmtiUniquePtr<char[]> name_uptr;
- if (self != nullptr) {
+ if (target != nullptr) {
// Have a native thread object, this thread is alive.
std::string name;
- self->GetThreadName(name);
+ target->GetThreadName(name);
jvmtiError name_result;
name_uptr = CopyString(env, name.c_str(), &name_result);
if (name_uptr == nullptr) {
@@ -209,11 +198,11 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI
}
info_ptr->name = name_uptr.get();
- info_ptr->priority = self->GetNativePriority();
+ info_ptr->priority = target->GetNativePriority();
- info_ptr->is_daemon = self->IsDaemon();
+ info_ptr->is_daemon = target->IsDaemon();
- art::ObjPtr<art::mirror::Object> peer = self->GetPeerFromOtherThread();
+ art::ObjPtr<art::mirror::Object> peer = target->GetPeerFromOtherThread();
// ThreadGroup.
if (peer != nullptr) {
@@ -310,9 +299,8 @@ struct InternalThreadState {
static InternalThreadState GetNativeThreadState(jthread thread,
const art::ScopedObjectAccessAlreadyRunnable& soa)
REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(art::Locks::user_code_suspension_lock_) {
+ REQUIRES(art::Locks::thread_list_lock_, art::Locks::user_code_suspension_lock_) {
art::Thread* self = nullptr;
- art::MutexLock tll_mu(soa.Self(), *art::Locks::thread_list_lock_);
if (thread == nullptr) {
self = art::Thread::Current();
} else {
@@ -456,43 +444,46 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED,
}
}
art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
state = GetNativeThreadState(thread, soa);
- break;
- } while (true);
-
- if (state.art_state == art::ThreadState::kStarting) {
- if (thread == nullptr) {
- // No native thread, and no Java thread? We must be starting up. Report as wrong phase.
- return ERR(WRONG_PHASE);
+ if (state.art_state == art::ThreadState::kStarting) {
+ break;
}
+ DCHECK(state.native_thread != nullptr);
- art::ScopedObjectAccess soa(self);
+ // Translate internal thread state to JVMTI and Java state.
+ jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
+
+ // Java state is derived from nativeGetState.
+ // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
+ // different mask if a thread got suspended due to user-code. However, this is for
+ // consistency with the Java view.
+ jint java_state = GetJavaStateFromInternal(state);
+
+ *thread_state_ptr = jvmti_state | java_state;
- // Need to read the Java "started" field to know whether this is starting or terminated.
- art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
- art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
- art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z");
- CHECK(started_field != nullptr);
- bool started = started_field->GetBoolean(peer) != 0;
- constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
- constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
- JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
- *thread_state_ptr = started ? kTerminatedState : kStartedState;
return ERR(NONE);
- }
- DCHECK(state.native_thread != nullptr);
+ } while (true);
- // Translate internal thread state to JVMTI and Java state.
- jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
+ DCHECK_EQ(state.art_state, art::ThreadState::kStarting);
- // Java state is derived from nativeGetState.
- // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
- // different mask if a thread got suspended due to user-code. However, this is for
- // consistency with the Java view.
- jint java_state = GetJavaStateFromInternal(state);
+ if (thread == nullptr) {
+ // No native thread, and no Java thread? We must be starting up. Report as wrong phase.
+ return ERR(WRONG_PHASE);
+ }
- *thread_state_ptr = jvmti_state | java_state;
+ art::ScopedObjectAccess soa(self);
+ // Need to read the Java "started" field to know whether this is starting or terminated.
+ art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
+ art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
+ art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z");
+ CHECK(started_field != nullptr);
+ bool started = started_field->GetBoolean(peer) != 0;
+ constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
+ constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
+ JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
+ *thread_state_ptr = started ? kTerminatedState : kStartedState;
return ERR(NONE);
}
@@ -571,7 +562,7 @@ jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env, jthread thread, cons
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
art::MutexLock mu(self, *art::Locks::thread_list_lock_);
- art::Thread* target = GetNativeThreadLocked(thread, soa);
+ art::Thread* target = GetNativeThread(thread, soa);
if (target == nullptr && thread == nullptr) {
return ERR(INVALID_THREAD);
}
@@ -600,7 +591,7 @@ jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env,
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
art::MutexLock mu(self, *art::Locks::thread_list_lock_);
- art::Thread* target = GetNativeThreadLocked(thread, soa);
+ art::Thread* target = GetNativeThread(thread, soa);
if (target == nullptr && thread == nullptr) {
return ERR(INVALID_THREAD);
}
@@ -700,8 +691,7 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env,
}
jvmtiError ThreadUtil::SuspendOther(art::Thread* self,
- jthread target_jthread,
- const art::Thread* target) {
+ jthread target_jthread) {
// Loop since we need to bail out and try again if we would end up getting suspended while holding
// the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we
// release the lock, wait to get resumed and try again.
@@ -714,33 +704,43 @@ jvmtiError ThreadUtil::SuspendOther(art::Thread* self,
SuspendCheck(self);
art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
{
- art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
// Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
// a user-code suspension. We retry and do another SuspendCheck to clear this.
if (self->GetUserCodeSuspendCount() != 0) {
continue;
- } else if (target->GetUserCodeSuspendCount() != 0) {
- return ERR(THREAD_SUSPENDED);
}
+ // We are not going to be suspended by user code from now on.
}
- bool timeout = true;
- while (timeout) {
+ {
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock thread_list_mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = GetNativeThread(target_jthread, soa);
art::ThreadState state = target->GetState();
if (state == art::ThreadState::kTerminated || state == art::ThreadState::kStarting) {
return ERR(THREAD_NOT_ALIVE);
- }
- art::Thread* ret_target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer(
- target_jthread,
- /* request_suspension */ true,
- art::SuspendReason::kForUserCode,
- &timeout);
- if (ret_target == nullptr && !timeout) {
- // TODO It would be good to get more information about why exactly the thread failed to
- // suspend.
- return ERR(INTERNAL);
+ } else {
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target->GetUserCodeSuspendCount() != 0) {
+ return ERR(THREAD_SUSPENDED);
+ }
}
}
- return OK;
+ bool timeout = true;
+ art::Thread* ret_target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer(
+ target_jthread,
+ /* request_suspension */ true,
+ art::SuspendReason::kForUserCode,
+ &timeout);
+ if (ret_target == nullptr && !timeout) {
+ // TODO It would be good to get more information about why exactly the thread failed to
+ // suspend.
+ return ERR(INTERNAL);
+ } else if (!timeout) {
+ // we didn't time out and got a result.
+ return OK;
+ }
+ // We timed out. Just go around and try again.
} while (true);
UNREACHABLE();
}
@@ -769,18 +769,21 @@ jvmtiError ThreadUtil::SuspendSelf(art::Thread* self) {
jvmtiError ThreadUtil::SuspendThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
art::Thread* self = art::Thread::Current();
- art::Thread* target;
+ bool target_is_self = false;
{
art::ScopedObjectAccess soa(self);
- target = GetNativeThread(thread, soa);
- }
- if (target == nullptr) {
- return ERR(INVALID_THREAD);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = GetNativeThread(thread, soa);
+ if (target == nullptr) {
+ return ERR(INVALID_THREAD);
+ } else if (target == self) {
+ target_is_self = true;
+ }
}
- if (target == self) {
+ if (target_is_self) {
return SuspendSelf(self);
} else {
- return SuspendOther(self, thread, target);
+ return SuspendOther(self, thread);
}
}
@@ -791,41 +794,56 @@ jvmtiError ThreadUtil::ResumeThread(jvmtiEnv* env ATTRIBUTE_UNUSED,
}
art::Thread* self = art::Thread::Current();
art::Thread* target;
- {
- // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
- // have the 'suspend_lock' locked here.
- art::ScopedObjectAccess soa(self);
- target = GetNativeThread(thread, soa);
- }
- if (target == nullptr) {
- return ERR(INVALID_THREAD);
- } else if (target == self) {
- // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so we
- // can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs about
- // current state since it's all concurrent.
- return ERR(THREAD_NOT_SUSPENDED);
- }
- // Now that we know we aren't getting suspended ourself (since we have a mutator lock) we lock the
- // suspend_lock to start suspending.
- art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
- {
- // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really cannot
- // tell why resume failed.
- art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
- if (target->GetUserCodeSuspendCount() == 0) {
- return ERR(THREAD_NOT_SUSPENDED);
+ // Retry until we know we won't get suspended by user code while resuming something.
+ do {
+ SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ {
+ art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
+ // a user-code suspension. We retry and do another SuspendCheck to clear this.
+ if (self->GetUserCodeSuspendCount() != 0) {
+ continue;
+ }
}
- }
- if (target->GetState() == art::ThreadState::kTerminated) {
- return ERR(THREAD_NOT_ALIVE);
- }
- DCHECK(target != self);
- if (!art::Runtime::Current()->GetThreadList()->Resume(target, art::SuspendReason::kForUserCode)) {
- // TODO Give a better error.
- // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
- return ERR(INTERNAL);
- }
- return OK;
+ // From now on we know we cannot get suspended by user-code.
+ {
+ // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+ // have the 'suspend_lock' locked here.
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ target = GetNativeThread(thread, soa);
+ if (target == nullptr) {
+ return ERR(INVALID_THREAD);
+ } else if (target == self) {
+ // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so
+ // we can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs
+ // about current state since it's all concurrent.
+ return ERR(THREAD_NOT_SUSPENDED);
+ } else if (target->GetState() == art::ThreadState::kTerminated) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really
+ // cannot tell why resume failed.
+ {
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target->GetUserCodeSuspendCount() == 0) {
+ return ERR(THREAD_NOT_SUSPENDED);
+ }
+ }
+ }
+ // It is okay that we don't have a thread_list_lock here since we know that the thread cannot
+ // die since it is currently held suspended by a SuspendReason::kForUserCode suspend.
+ DCHECK(target != self);
+ if (!art::Runtime::Current()->GetThreadList()->Resume(target,
+ art::SuspendReason::kForUserCode)) {
+ // TODO Give a better error.
+ // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
+ return ERR(INTERNAL);
+ } else {
+ return OK;
+ }
+ } while (true);
}
// Suspends all the threads in the list at the same time. Getting this behavior is a little tricky
@@ -851,6 +869,7 @@ jvmtiError ThreadUtil::SuspendThreadList(jvmtiEnv* env,
for (jint i = 0; i < request_count; i++) {
{
art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
if (threads[i] == nullptr || GetNativeThread(threads[i], soa) == self) {
current_thread_indexes.push_back(i);
continue;
diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h
index 0f7e8379fd..bf566380c0 100644
--- a/runtime/openjdkjvmti/ti_thread.h
+++ b/runtime/openjdkjvmti/ti_thread.h
@@ -35,10 +35,12 @@
#include "jni.h"
#include "jvmti.h"
+#include "base/macros.h"
#include "base/mutex.h"
namespace art {
class ArtField;
+class ScopedObjectAccessAlreadyRunnable;
class Thread;
} // namespace art
@@ -86,6 +88,11 @@ class ThreadUtil {
const jthread* threads,
jvmtiError* results);
+ static art::Thread* GetNativeThread(jthread thread,
+ const art::ScopedObjectAccessAlreadyRunnable& soa)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(art::Locks::thread_list_lock_);
+
private:
// We need to make sure only one thread tries to suspend threads at a time so we can get the
// 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a
@@ -98,9 +105,7 @@ class ThreadUtil {
// cause the thread to wake up if the thread is suspended for the debugger or gc or something.
static jvmtiError SuspendSelf(art::Thread* self)
REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_);
- static jvmtiError SuspendOther(art::Thread* self,
- jthread target_jthread,
- const art::Thread* target)
+ static jvmtiError SuspendOther(art::Thread* self, jthread target_jthread)
REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_);
static art::ArtField* context_class_loader_;
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 6f1d15c767..f28f0cabe2 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -238,8 +238,7 @@ class ArgArray {
// TODO: The method's parameter's type must have been previously resolved, yet
// we've seen cases where it's not b/34440020.
ObjPtr<mirror::Class> dst_class(
- m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
- true /* resolve */));
+ m->ResolveClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_));
if (dst_class.Ptr() == nullptr) {
CHECK(self->IsExceptionPending());
return false;
@@ -378,7 +377,7 @@ static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args)
Thread* const self = Thread::Current();
for (uint32_t i = 0; i < num_params; i++) {
dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
- ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */));
+ ObjPtr<mirror::Class> param_type(m->ResolveClassFromTypeIndex(type_idx));
if (param_type == nullptr) {
CHECK(self->IsExceptionPending());
LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 27124190c7..a8ccf89cb2 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -38,6 +38,7 @@
#include "android-base/strings.h"
+#include "aot_class_linker.h"
#include "arch/arm/quick_method_frame_info_arm.h"
#include "arch/arm/registers_arm.h"
#include "arch/arm64/quick_method_frame_info_arm64.h"
@@ -63,7 +64,6 @@
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
-#include "cha.h"
#include "class_linker-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
@@ -238,7 +238,7 @@ Runtime::Runtime()
system_thread_group_(nullptr),
system_class_loader_(nullptr),
dump_gc_performance_on_shutdown_(false),
- preinitialization_transaction_(nullptr),
+ preinitialization_transactions_(),
verify_(verifier::VerifyMode::kNone),
allow_dex_file_fallback_(true),
target_sdk_version_(0),
@@ -259,8 +259,7 @@ Runtime::Runtime()
pruned_dalvik_cache_(false),
// Initially assume we perceive jank in case the process state is never updated.
process_state_(kProcessStateJankPerceptible),
- zygote_no_threads_(false),
- cha_(nullptr) {
+ zygote_no_threads_(false) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
@@ -382,7 +381,6 @@ Runtime::~Runtime() {
delete monitor_list_;
delete monitor_pool_;
delete class_linker_;
- delete cha_;
delete heap_;
delete intern_table_;
delete oat_file_manager_;
@@ -1286,8 +1284,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
GetHeap()->EnableObjectValidation();
CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
- class_linker_ = new ClassLinker(intern_table_);
- cha_ = new ClassHierarchyAnalysis;
+ if (UNLIKELY(IsAotCompiler())) {
+ class_linker_ = new AotClassLinker(intern_table_);
+ } else {
+ class_linker_ = new ClassLinker(intern_table_);
+ }
if (GetHeap()->HasBootImageSpace()) {
bool result = class_linker_->InitFromBootImage(&error_msg);
if (!result) {
@@ -1833,8 +1834,8 @@ void Runtime::VisitConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags) {
}
void Runtime::VisitTransactionRoots(RootVisitor* visitor) {
- if (preinitialization_transaction_ != nullptr) {
- preinitialization_transaction_->VisitRoots(visitor);
+ for (auto& transaction : preinitialization_transactions_) {
+ transaction->VisitRoots(visitor);
}
}
@@ -2066,28 +2067,32 @@ void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
}
// Transaction support.
+bool Runtime::IsActiveTransaction() const {
+ return !preinitialization_transactions_.empty() && !GetTransaction()->IsRollingBack();
+}
+
void Runtime::EnterTransactionMode() {
DCHECK(IsAotCompiler());
DCHECK(!IsActiveTransaction());
- preinitialization_transaction_ = std::make_unique<Transaction>();
+ preinitialization_transactions_.push_back(std::make_unique<Transaction>());
}
-void Runtime::EnterTransactionMode(mirror::Class* root) {
+void Runtime::EnterTransactionMode(bool strict, mirror::Class* root) {
DCHECK(IsAotCompiler());
- preinitialization_transaction_ = std::make_unique<Transaction>(root);
+ preinitialization_transactions_.push_back(std::make_unique<Transaction>(strict, root));
}
void Runtime::ExitTransactionMode() {
DCHECK(IsAotCompiler());
- preinitialization_transaction_ = nullptr;
+ DCHECK(IsActiveTransaction());
+ preinitialization_transactions_.pop_back();
}
void Runtime::RollbackAndExitTransactionMode() {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- std::unique_ptr<Transaction> rollback_transaction_= std::move(preinitialization_transaction_);
- ExitTransactionMode();
- rollback_transaction_->Rollback();
+ preinitialization_transactions_.back()->Rollback();
+ preinitialization_transactions_.pop_back();
}
bool Runtime::IsTransactionAborted() const {
@@ -2095,67 +2100,86 @@ bool Runtime::IsTransactionAborted() const {
return false;
} else {
DCHECK(IsAotCompiler());
- return preinitialization_transaction_->IsAborted();
+ return GetTransaction()->IsAborted();
}
}
+void Runtime::RollbackAllTransactions() {
+ // If transaction is aborted, all transactions will be kept in the list.
+ // Rollback and exit all of them.
+ while (IsActiveTransaction()) {
+ RollbackAndExitTransactionMode();
+ }
+}
+
+bool Runtime::IsActiveStrictTransactionMode() const {
+ return IsActiveTransaction() && GetTransaction()->IsStrict();
+}
+
+const std::unique_ptr<Transaction>& Runtime::GetTransaction() const {
+ DCHECK(!preinitialization_transactions_.empty());
+ return preinitialization_transactions_.back();
+}
+
void Runtime::AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
// Throwing an exception may cause its class initialization. If we mark the transaction
// aborted before that, we may warn with a false alarm. Throwing the exception before
// marking the transaction aborted avoids that.
- preinitialization_transaction_->ThrowAbortError(self, &abort_message);
- preinitialization_transaction_->Abort(abort_message);
+ // But now the transaction can be nested, and abort the transaction will relax the constraints
+ // for constructing stack trace.
+ GetTransaction()->Abort(abort_message);
+ GetTransaction()->ThrowAbortError(self, &abort_message);
}
void Runtime::ThrowTransactionAbortError(Thread* self) {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
// Passing nullptr means we rethrow an exception with the earlier transaction abort message.
- preinitialization_transaction_->ThrowAbortError(self, nullptr);
+ GetTransaction()->ThrowAbortError(self, nullptr);
}
void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
uint8_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset,
int8_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset,
uint16_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset,
int16_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
uint32_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteField32(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteField32(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset,
uint64_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteField64(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteField64(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldReference(mirror::Object* obj,
@@ -2164,7 +2188,7 @@ void Runtime::RecordWriteFieldReference(mirror::Object* obj,
bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldReference(obj,
+ GetTransaction()->RecordWriteFieldReference(obj,
field_offset,
value.Ptr(),
is_volatile);
@@ -2173,38 +2197,38 @@ void Runtime::RecordWriteFieldReference(mirror::Object* obj,
void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteArray(array, index, value);
+ GetTransaction()->RecordWriteArray(array, index, value);
}
void Runtime::RecordStrongStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordStrongStringInsertion(s);
+ GetTransaction()->RecordStrongStringInsertion(s);
}
void Runtime::RecordWeakStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWeakStringInsertion(s);
+ GetTransaction()->RecordWeakStringInsertion(s);
}
void Runtime::RecordStrongStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordStrongStringRemoval(s);
+ GetTransaction()->RecordStrongStringRemoval(s);
}
void Runtime::RecordWeakStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWeakStringRemoval(s);
+ GetTransaction()->RecordWeakStringRemoval(s);
}
void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache,
dex::StringIndex string_idx) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordResolveString(dex_cache, string_idx);
+ GetTransaction()->RecordResolveString(dex_cache, string_idx);
}
void Runtime::SetFaultMessage(const std::string& message) {
@@ -2422,5 +2446,4 @@ void Runtime::DeoptimizeBootImage() {
GetClassLinker()->VisitClasses(&visitor);
}
}
-
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9424596c8a..0c1344e11a 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -73,7 +73,6 @@ namespace verifier {
class ArenaPool;
class ArtMethod;
enum class CalleeSaveType: uint32_t;
-class ClassHierarchyAnalysis;
class ClassLinker;
class CompilerCallbacks;
class DexFile;
@@ -455,16 +454,17 @@ class Runtime {
const std::string& profile_output_filename);
// Transaction support.
- bool IsActiveTransaction() const {
- return preinitialization_transaction_ != nullptr;
- }
+ bool IsActiveTransaction() const;
void EnterTransactionMode();
- void EnterTransactionMode(mirror::Class* root);
+ void EnterTransactionMode(bool strict, mirror::Class* root);
void ExitTransactionMode();
+ void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_);
// Transaction rollback and exit transaction are always done together, it's convenience to
// do them in one function.
void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_);
bool IsTransactionAborted() const;
+ const std::unique_ptr<Transaction>& GetTransaction() const;
+ bool IsActiveStrictTransactionMode() const;
void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -650,10 +650,6 @@ class Runtime {
void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
- ClassHierarchyAnalysis* GetClassHierarchyAnalysis() {
- return cha_;
- }
-
void AttachAgent(const std::string& agent_arg);
const std::list<ti::Agent>& GetAgents() const {
@@ -846,8 +842,11 @@ class Runtime {
// If true, then we dump the GC cumulative timings on shutdown.
bool dump_gc_performance_on_shutdown_;
- // Transaction used for pre-initializing classes at compilation time.
- std::unique_ptr<Transaction> preinitialization_transaction_;
+ // Transactions used for pre-initializing classes at compilation time.
+ // Support nested transactions, maintain a list containing all transactions. Transactions are
+ // handled under a stack discipline. Because GC needs to go over all transactions, we choose list
+ // as substantial data structure instead of stack.
+ std::list<std::unique_ptr<Transaction>> preinitialization_transactions_;
// If kNone, verification is disabled. kEnable by default.
verifier::VerifyMode verify_;
@@ -944,8 +943,6 @@ class Runtime {
// Generic system-weak holders.
std::vector<gc::AbstractSystemWeakHolder*> system_weak_holders_;
- ClassHierarchyAnalysis* cha_;
-
std::unique_ptr<RuntimeCallbacks> callbacks_;
std::atomic<uint32_t> deoptimization_counts_[
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 9e62aa6b55..50deb1f913 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -34,11 +34,15 @@ namespace art {
static constexpr bool kEnableTransactionStats = false;
Transaction::Transaction()
- : log_lock_("transaction log lock", kTransactionLogLock), aborted_(false) {
+ : log_lock_("transaction log lock", kTransactionLogLock),
+ aborted_(false),
+ rolling_back_(false),
+ strict_(false) {
CHECK(Runtime::Current()->IsAotCompiler());
}
-Transaction::Transaction(mirror::Class* root) : Transaction() {
+Transaction::Transaction(bool strict, mirror::Class* root) : Transaction() {
+ strict_ = strict;
root_ = root;
}
@@ -101,6 +105,15 @@ bool Transaction::IsAborted() {
return aborted_;
}
+bool Transaction::IsRollingBack() {
+ return rolling_back_;
+}
+
+bool Transaction::IsStrict() {
+ MutexLock mu(Thread::Current(), log_lock_);
+ return strict_;
+}
+
const std::string& Transaction::GetAbortMessage() {
MutexLock mu(Thread::Current(), log_lock_);
return abort_message_;
@@ -226,15 +239,17 @@ void Transaction::LogInternedString(InternStringLog&& log) {
}
void Transaction::Rollback() {
- CHECK(!Runtime::Current()->IsActiveTransaction());
Thread* self = Thread::Current();
self->AssertNoPendingException();
MutexLock mu1(self, *Locks::intern_table_lock_);
MutexLock mu2(self, log_lock_);
+ rolling_back_ = true;
+ CHECK(!Runtime::Current()->IsActiveTransaction());
UndoObjectModifications();
UndoArrayModifications();
UndoInternStringTableModifications();
UndoResolveStringModifications();
+ rolling_back_ = false;
}
void Transaction::UndoObjectModifications() {
@@ -416,7 +431,7 @@ void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj,
const FieldValue& field_value) const {
// TODO We may want to abort a transaction while still being in transaction mode. In this case,
// we'd need to disable the check.
- constexpr bool kCheckTransaction = true;
+ constexpr bool kCheckTransaction = false;
switch (field_value.kind) {
case kBoolean:
if (UNLIKELY(field_value.is_volatile)) {
@@ -595,30 +610,39 @@ void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array,
uint64_t value) const {
// TODO We may want to abort a transaction while still being in transaction mode. In this case,
// we'd need to disable the check.
+ constexpr bool kCheckTransaction = false;
switch (array_type) {
case Primitive::kPrimBoolean:
- array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
+ array->AsBooleanArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<uint8_t>(value));
break;
case Primitive::kPrimByte:
- array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
+ array->AsByteArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int8_t>(value));
break;
case Primitive::kPrimChar:
- array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
+ array->AsCharArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<uint16_t>(value));
break;
case Primitive::kPrimShort:
- array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
+ array->AsShortArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int16_t>(value));
break;
case Primitive::kPrimInt:
- array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
+ array->AsIntArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int32_t>(value));
break;
case Primitive::kPrimFloat:
- array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
+ array->AsFloatArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<float>(value));
break;
case Primitive::kPrimLong:
- array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
+ array->AsLongArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int64_t>(value));
break;
case Primitive::kPrimDouble:
- array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
+ array->AsDoubleArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<double>(value));
break;
case Primitive::kPrimNot:
LOG(FATAL) << "ObjectArray should be treated as Object";
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 22518f6c7e..64349de2e9 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -45,7 +45,7 @@ class Transaction FINAL {
static constexpr const char* kAbortExceptionSignature = "Ldalvik/system/TransactionAbortError;";
Transaction();
- explicit Transaction(mirror::Class* root);
+ explicit Transaction(bool strict, mirror::Class* root);
~Transaction();
void Abort(const std::string& abort_message)
@@ -56,6 +56,15 @@ class Transaction FINAL {
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsAborted() REQUIRES(!log_lock_);
+ // If the transaction is rollbacking. Transactions will set this flag when they start rollbacking,
+ // because the nested transaction should be disabled when rollbacking to restore the memory.
+ bool IsRollingBack();
+
+ // If the transaction is in strict mode, then all access of static fields will be constrained,
+ // one class's clinit will not be allowed to read or modify another class's static fields, unless
+ // the transaction is aborted.
+ bool IsStrict() REQUIRES(!log_lock_);
+
// Record object field changes.
void RecordWriteFieldBoolean(mirror::Object* obj,
MemberOffset field_offset,
@@ -289,6 +298,8 @@ class Transaction FINAL {
std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
std::list<ResolveStringLog> resolve_string_logs_ GUARDED_BY(log_lock_);
bool aborted_ GUARDED_BY(log_lock_);
+ bool rolling_back_; // Single thread, no race.
+ bool strict_ GUARDED_BY(log_lock_);
std::string abort_message_ GUARDED_BY(log_lock_);
mirror::Class* root_ GUARDED_BY(log_lock_);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 6149f0d94e..0c460db7da 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2899,10 +2899,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range);
const RegType* return_type = nullptr;
if (called_method != nullptr) {
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+ ObjPtr<mirror::Class> return_type_class = can_load_classes_
+ ? called_method->ResolveReturnType()
+ : called_method->LookupResolvedReturnType();
if (return_type_class != nullptr) {
return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
- return_type_class,
+ return_type_class.Ptr(),
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
@@ -2942,10 +2944,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
} else {
is_constructor = called_method->IsConstructor();
return_type_descriptor = called_method->GetReturnTypeDescriptor();
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+ ObjPtr<mirror::Class> return_type_class = can_load_classes_
+ ? called_method->ResolveReturnType()
+ : called_method->LookupResolvedReturnType();
if (return_type_class != nullptr) {
return_type = &FromClass(return_type_descriptor,
- return_type_class,
+ return_type_class.Ptr(),
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
@@ -5261,10 +5265,12 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() {
const RegType& MethodVerifier::GetMethodReturnType() {
if (return_type_ == nullptr) {
if (mirror_method_ != nullptr) {
- mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
+ ObjPtr<mirror::Class> return_type_class = can_load_classes_
+ ? mirror_method_->ResolveReturnType()
+ : mirror_method_->LookupResolvedReturnType();
if (return_type_class != nullptr) {
return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
- return_type_class,
+ return_type_class.Ptr(),
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 112eec847d..470b0b3d58 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -33,7 +33,8 @@
namespace art {
namespace verifier {
-VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) {
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, bool output_only)
+ : output_only_(output_only) {
for (const DexFile* dex_file : dex_files) {
DCHECK(GetDexFileDeps(*dex_file) == nullptr);
std::unique_ptr<DexFileDeps> deps(new DexFileDeps());
@@ -41,6 +42,9 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) {
}
}
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files)
+ : VerifierDeps(dex_files, /*output_only*/ true) {}
+
void VerifierDeps::MergeWith(const VerifierDeps& other,
const std::vector<const DexFile*>& dex_files) {
DCHECK(dex_deps_.size() == other.dex_deps_.size());
@@ -694,7 +698,7 @@ void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
ArrayRef<const uint8_t> data)
- : VerifierDeps(dex_files) {
+ : VerifierDeps(dex_files, /*output_only*/ false) {
if (data.empty()) {
// Return eagerly, as the first thing we expect from VerifierDeps data is
// the number of created strings, even if there is no dependency.
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index b883a9e642..2d452f6d6b 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -121,6 +121,10 @@ class VerifierDeps {
return GetDexFileDeps(dex_file)->unverified_classes_;
}
+ bool OutputOnly() const {
+ return output_only_;
+ }
+
private:
static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
@@ -198,6 +202,8 @@ class VerifierDeps {
bool Equals(const DexFileDeps& rhs) const;
};
+ VerifierDeps(const std::vector<const DexFile*>& dex_files, bool output_only);
+
// Finds the DexFileDep instance associated with `dex_file`, or nullptr if
// `dex_file` is not reported as being compiled.
DexFileDeps* GetDexFileDeps(const DexFile& dex_file);
@@ -321,6 +327,9 @@ class VerifierDeps {
// Map from DexFiles into dependencies collected from verification of their methods.
std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_;
+ // Output only signifies if we are using the verifier deps to verify or just to generate them.
+ const bool output_only_;
+
friend class VerifierDepsTest;
ART_FRIEND_TEST(VerifierDepsTest, StringToId);
ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode);
diff --git a/runtime/simulator/Android.bp b/simulator/Android.bp
index 03e3f1562a..a39928985a 100644
--- a/runtime/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -14,6 +14,12 @@
// limitations under the License.
//
+cc_library_headers {
+ name: "libart_simulator_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
cc_defaults {
name: "libart_simulator_defaults",
host_supported: true,
@@ -29,8 +35,8 @@ cc_defaults {
"liblog",
],
cflags: ["-DVIXL_INCLUDE_SIMULATOR_AARCH64"],
- export_include_dirs: ["."],
- include_dirs: ["art/runtime"],
+
+ header_libs: ["libart_simulator_headers"],
}
art_cc_library {
@@ -53,3 +59,38 @@ art_cc_library {
"libvixld-arm64",
],
}
+
+cc_defaults {
+ name: "libart_simulator_container_defaults",
+ host_supported: true,
+
+ defaults: ["art_defaults"],
+ srcs: [
+ "code_simulator_container.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+
+ header_libs: ["libart_simulator_headers"],
+ export_include_dirs: ["."], // TODO: Consider a proper separation.
+}
+
+art_cc_library {
+ name: "libart-simulator-container",
+ defaults: ["libart_simulator_container_defaults"],
+ shared_libs: [
+ "libart",
+ ],
+}
+
+art_cc_library {
+ name: "libartd-simulator-container",
+ defaults: [
+ "art_debug_defaults",
+ "libart_simulator_container_defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ ],
+}
diff --git a/runtime/simulator/code_simulator.cc b/simulator/code_simulator.cc
index 1a1116050e..e653dfc4fe 100644
--- a/runtime/simulator/code_simulator.cc
+++ b/simulator/code_simulator.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "simulator/code_simulator.h"
-#include "simulator/code_simulator_arm64.h"
+#include "code_simulator.h"
+
+#include "code_simulator_arm64.h"
namespace art {
diff --git a/runtime/simulator/code_simulator_arm64.cc b/simulator/code_simulator_arm64.cc
index c7ad1fd8aa..939d2e287f 100644
--- a/runtime/simulator/code_simulator_arm64.cc
+++ b/simulator/code_simulator_arm64.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "simulator/code_simulator_arm64.h"
+#include "code_simulator_arm64.h"
#include "base/logging.h"
diff --git a/runtime/simulator/code_simulator_arm64.h b/simulator/code_simulator_arm64.h
index 59ea34fb80..0542593eb2 100644
--- a/runtime/simulator/code_simulator_arm64.h
+++ b/simulator/code_simulator_arm64.h
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
-#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#ifndef ART_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#define ART_SIMULATOR_CODE_SIMULATOR_ARM64_H_
#include "memory"
-#include "simulator/code_simulator.h"
// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
@@ -26,6 +25,8 @@
#include "aarch64/simulator-aarch64.h"
#pragma GCC diagnostic pop
+#include "code_simulator.h"
+
namespace art {
namespace arm64 {
@@ -55,4 +56,4 @@ class CodeSimulatorArm64 : public CodeSimulator {
} // namespace arm64
} // namespace art
-#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#endif // ART_SIMULATOR_CODE_SIMULATOR_ARM64_H_
diff --git a/runtime/code_simulator_container.cc b/simulator/code_simulator_container.cc
index d884c58782..a5f05dc8fc 100644
--- a/runtime/code_simulator_container.cc
+++ b/simulator/code_simulator_container.cc
@@ -17,6 +17,8 @@
#include <dlfcn.h>
#include "code_simulator_container.h"
+
+#include "code_simulator.h"
#include "globals.h"
namespace art {
diff --git a/runtime/code_simulator_container.h b/simulator/code_simulator_container.h
index 10178bac67..31a915e4f1 100644
--- a/runtime/code_simulator_container.h
+++ b/simulator/code_simulator_container.h
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
-#define ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
+#ifndef ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_
+#define ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_
#include "arch/instruction_set.h"
#include "base/logging.h"
-#include "simulator/code_simulator.h"
namespace art {
+class CodeSimulator;
+
// This container dynamically opens and closes libart-simulator.
class CodeSimulatorContainer {
public:
@@ -52,4 +53,4 @@ class CodeSimulatorContainer {
} // namespace art
-#endif // ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
+#endif // ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_
diff --git a/runtime/simulator/code_simulator.h b/simulator/include/code_simulator.h
index bd48909e41..256ab23aa4 100644
--- a/runtime/simulator/code_simulator.h
+++ b/simulator/include/code_simulator.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
-#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
+#ifndef ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
+#define ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
#include "arch/instruction_set.h"
@@ -43,4 +43,4 @@ extern "C" CodeSimulator* CreateCodeSimulator(InstructionSet target_isa);
} // namespace art
-#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
+#endif // ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
diff --git a/test/1911-get-local-var-table/expected.txt b/test/1911-get-local-var-table/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/1911-get-local-var-table/expected.txt
diff --git a/test/1911-get-local-var-table/info.txt b/test/1911-get-local-var-table/info.txt
new file mode 100644
index 0000000000..43955e5456
--- /dev/null
+++ b/test/1911-get-local-var-table/info.txt
@@ -0,0 +1 @@
+Tests getting local variable table from JVMTI
diff --git a/test/1911-get-local-var-table/run b/test/1911-get-local-var-table/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1911-get-local-var-table/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1911-get-local-var-table/src/Main.java b/test/1911-get-local-var-table/src/Main.java
new file mode 100644
index 0000000000..4e0c9e4506
--- /dev/null
+++ b/test/1911-get-local-var-table/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1911.run();
+ }
+}
diff --git a/test/1911-get-local-var-table/src/art/Breakpoint.java b/test/1911-get-local-var-table/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1911-get-local-var-table/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1911-get-local-var-table/src/art/Locals.java b/test/1911-get-local-var-table/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1911-get-local-var-table/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1911-get-local-var-table/src/art/Suspension.java b/test/1911-get-local-var-table/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1911-get-local-var-table/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1911-get-local-var-table/src/art/Test1911.java b/test/1911-get-local-var-table/src/art/Test1911.java
new file mode 100644
index 0000000000..4dd9054bf0
--- /dev/null
+++ b/test/1911-get-local-var-table/src/art/Test1911.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Test1911 {
+ // Class/dex file containing the following class.
+ //
+ // CLASS_BYTES generated with java version 1.8.0_45: javac -g art/Target.java
+ // DEX_BYTES generated with dx version 1.14: dx --dex --output=./classes.dex art/Target.class
+ //
+ // package art;
+ // import java.util.ArrayList;
+ // public class Target {
+ // public int zzz;
+ // public Target(int xxx) {
+ // int q = xxx * 4;
+ // zzz = q;
+ // }
+ // public static void doNothing(Object... objs) { doNothing(objs); }
+ // public void doSomething(int x) {
+ // doNothing(this);
+ // int y = x + 3;
+ // for (int z = 0; z < y * x; z++) {
+ // float q = y - z;
+ // double i = 0.3d * q;
+ // doNothing(q, i);
+ // }
+ // Object o = new Object();
+ // ArrayList<Integer> i = new ArrayList<>();
+ // int p = 4 | x;
+ // long q = 3 * p;
+ // doNothing(p, q, o, i);
+ // }
+ // }
+ public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQARgoABAAuCQANAC8KAA0AMAcAMQY/0zMzMzMzMwoAMgAzCgA0ADUHADYKAAkALgoA" +
+ "NwA4CgA5ADoHADsBAAN6enoBAAFJAQAGPGluaXQ+AQAEKEkpVgEABENvZGUBAA9MaW5lTnVtYmVy" +
+ "VGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAMTGFydC9UYXJnZXQ7AQADeHh4AQAB" +
+ "cQEACWRvTm90aGluZwEAFihbTGphdmEvbGFuZy9PYmplY3Q7KVYBAARvYmpzAQATW0xqYXZhL2xh" +
+ "bmcvT2JqZWN0OwEAC2RvU29tZXRoaW5nAQABRgEAAWkBAAFEAQABegEAAXgBAAF5AQABbwEAEkxq" +
+ "YXZhL2xhbmcvT2JqZWN0OwEAFUxqYXZhL3V0aWwvQXJyYXlMaXN0OwEAAXABAAFKAQAWTG9jYWxW" +
+ "YXJpYWJsZVR5cGVUYWJsZQEAKkxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvSW50ZWdl" +
+ "cjs+OwEADVN0YWNrTWFwVGFibGUBAApTb3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMABAAPAwADgAP" +
+ "DAAZABoBABBqYXZhL2xhbmcvT2JqZWN0BwA9DAA+AD8HAEAMAD4AQQEAE2phdmEvdXRpbC9BcnJh" +
+ "eUxpc3QHAEIMAD4AQwcARAwAPgBFAQAKYXJ0L1RhcmdldAEAAygpVgEAD2phdmEvbGFuZy9GbG9h" +
+ "dAEAB3ZhbHVlT2YBABQoRilMamF2YS9sYW5nL0Zsb2F0OwEAEGphdmEvbGFuZy9Eb3VibGUBABUo" +
+ "RClMamF2YS9sYW5nL0RvdWJsZTsBABFqYXZhL2xhbmcvSW50ZWdlcgEAFihJKUxqYXZhL2xhbmcv" +
+ "SW50ZWdlcjsBAA5qYXZhL2xhbmcvTG9uZwEAEyhKKUxqYXZhL2xhbmcvTG9uZzsAIQANAAQAAAAB" +
+ "AAEADgAPAAAAAwABABAAEQABABIAAABYAAIAAwAAAA4qtwABGwdoPSoctQACsQAAAAIAEwAAABIA" +
+ "BAAAAAUABAAGAAgABwANAAgAFAAAACAAAwAAAA4AFQAWAAAAAAAOABcADwABAAgABgAYAA8AAgCJ" +
+ "ABkAGgABABIAAAAvAAEAAQAAAAUquAADsQAAAAIAEwAAAAYAAQAAAAkAFAAAAAwAAQAAAAUAGwAc" +
+ "AAAAAQAdABEAAQASAAABWAAFAAgAAACCBL0ABFkDKlO4AAMbBmA9Az4dHBtoogAvHB1khjgEFAAF" +
+ "FwSNazkFBb0ABFkDFwS4AAdTWQQYBbgACFO4AAOEAwGn/9C7AARZtwABTrsACVm3AAo6BAcbgDYF" +
+ "BhUFaIU3Bge9AARZAxUFuAALU1kEFga4AAxTWQUtU1kGGQRTuAADsQAAAAQAEwAAADYADQAAAAsA" +
+ "CwAMAA8ADQAYAA4AHgAPACcAEAA+AA0ARAASAEwAEwBVABQAWgAVAGEAFgCBABcAFAAAAGYACgAe" +
+ "ACAAGAAeAAQAJwAXAB8AIAAFABEAMwAhAA8AAwAAAIIAFQAWAAAAAACCACIADwABAA8AcwAjAA8A" +
+ "AgBMADYAJAAlAAMAVQAtAB8AJgAEAFoAKAAnAA8ABQBhACEAGAAoAAYAKQAAAAwAAQBVAC0AHwAq" +
+ "AAQAKwAAAAoAAv0AEQEB+gAyAAEALAAAAAIALQ==");
+ public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCQtgjEV631Ma/btYyIy2IzqHWNN+nZiwl0BQAAcAAAAHhWNBIAAAAAAAAAANQEAAAk" +
+ "AAAAcAAAAA0AAAAAAQAABwAAADQBAAABAAAAiAEAAAkAAACQAQAAAQAAANgBAAB8AwAA+AEAAB4D" +
+ "AAAmAwAAKQMAACwDAAAvAwAAMgMAADYDAAA6AwAAPgMAAEIDAABQAwAAZAMAAHcDAACMAwAAngMA" +
+ "ALIDAADJAwAA9QMAAAIEAAAFBAAACQQAAA0EAAAiBAAALQQAADoEAAA9BAAAQAQAAEYEAABJBAAA" +
+ "TAQAAFIEAABbBAAAXgQAAGMEAABmBAAAaQQAAAEAAAACAAAAAwAAAAQAAAAJAAAACgAAAAsAAAAM" +
+ "AAAADQAAAA4AAAAPAAAAEgAAABUAAAAFAAAABQAAAPgCAAAGAAAABgAAAAADAAAHAAAABwAAAAgD" +
+ "AAAIAAAACAAAABADAAASAAAACwAAAAAAAAATAAAACwAAAAgDAAAUAAAACwAAABgDAAAEAAIAIwAA" +
+ "AAQABQAAAAAABAAGABYAAAAEAAUAFwAAAAUAAAAeAAAABgABAB4AAAAHAAIAHgAAAAgAAwAeAAAA" +
+ "CQAEAAAAAAAKAAQAAAAAAAQAAAABAAAACQAAAAAAAAARAAAAAAAAAL4EAAAAAAAAAwACAAEAAABu" +
+ "BAAACAAAAHAQBwABANoAAgRZEAAADgABAAEAAQAAAHsEAAAEAAAAcRABAAAADgAQAAIAAgAAAIEE" +
+ "AABcAAAAEhkjmQwAEgpNDgkKcRABAAkA2AUPAxIIkgkFDzWYJACRCQUIgpYYCjMzMzMzM9M/iWyt" +
+ "AAoMEikjmQwAEgpxEAQABgAMC00LCQoSGnEgAwAQAAwLTQsJCnEQAQAJANgICAEo2yIDCQBwEAcA" +
+ "AwAiAgoAcBAIAAIA3gQPBNoJBAOBlhJJI5kMABIKcRAFAAQADAtNCwkKEhpxIAYAdgAMC00LCQoS" +
+ "Kk0DCQoSOk0CCQpxEAEACQAOAAEAAAAAAAAAAQAAAAEAAAABAAAAAgAAAAEAAAADAAAAAQAAAAwA" +
+ "Bjxpbml0PgABRAABRgABSQABSgACTEQAAkxGAAJMSQACTEoADExhcnQvVGFyZ2V0OwASTGphdmEv" +
+ "bGFuZy9Eb3VibGU7ABFMamF2YS9sYW5nL0Zsb2F0OwATTGphdmEvbGFuZy9JbnRlZ2VyOwAQTGph" +
+ "dmEvbGFuZy9Mb25nOwASTGphdmEvbGFuZy9PYmplY3Q7ABVMamF2YS91dGlsL0FycmF5TGlzdDsA" +
+ "KkxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvSW50ZWdlcjs+OwALVGFyZ2V0LmphdmEA" +
+ "AVYAAlZJAAJWTAATW0xqYXZhL2xhbmcvT2JqZWN0OwAJZG9Ob3RoaW5nAAtkb1NvbWV0aGluZwAB" +
+ "aQABbwAEb2JqcwABcAABcQAEdGhpcwAHdmFsdWVPZgABeAADeHh4AAF5AAF6AAN6enoABQEhBw48" +
+ "LQMAHQMtAAkBGwcOAAsBIAcOli0DBSIDAQEDCCMDSzwDBh0ChwMAGQEBFAtABQAFBloDAxoKWgQC" +
+ "GQsRLQMEHAM8AwYdBAEaDwAAAQIBAAEAgYAE+AMBiQGYBAIBsAQADQAAAAAAAAABAAAAAAAAAAEA" +
+ "AAAkAAAAcAAAAAIAAAANAAAAAAEAAAMAAAAHAAAANAEAAAQAAAABAAAAiAEAAAUAAAAJAAAAkAEA" +
+ "AAYAAAABAAAA2AEAAAEgAAADAAAA+AEAAAEQAAAFAAAA+AIAAAIgAAAkAAAAHgMAAAMgAAADAAAA" +
+ "bgQAAAAgAAABAAAAvgQAAAAQAAABAAAA1AQAAA==");
+
+
+ // The variables of the functions in the above Target class.
+ public static Set<Locals.VariableDescription>[] CONSTRUCTOR_VARIABLES = new Set[] {
+ // RI Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(8, 6, "q", "I", null, 2),
+ new Locals.VariableDescription(0, 14, "xxx", "I", null, 1),
+ new Locals.VariableDescription(0, 14, "this", "Lart/Target;", null, 0))),
+ // ART Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 8, "this", "Lart/Target;", null, 1),
+ new Locals.VariableDescription(5, 3, "q", "I", null, 0),
+ new Locals.VariableDescription(0, 8, "xxx", "I", null, 2))),
+ };
+
+ public static Set<Locals.VariableDescription>[] DO_NOTHING_VARIABLES = new Set[] {
+ // RI Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 5, "objs", "[Ljava/lang/Object;", null, 0))),
+ // ART Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 4, "objs", "[Ljava/lang/Object;", null, 0))),
+ };
+
+ public static Set<Locals.VariableDescription>[] DO_SOMETHING_VARIABLES = new Set[] {
+ // RI Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 130, "x", "I", null, 1),
+ new Locals.VariableDescription(76, 54, "o", "Ljava/lang/Object;", null, 3),
+ new Locals.VariableDescription(30, 32, "q", "F", null, 4),
+ new Locals.VariableDescription(39, 23, "i", "D", null, 5),
+ new Locals.VariableDescription(17, 51, "z", "I", null, 3),
+ new Locals.VariableDescription(15, 115, "y", "I", null, 2),
+ new Locals.VariableDescription(90, 40, "p", "I", null, 5),
+ new Locals.VariableDescription(97, 33, "q", "J", null, 6),
+ new Locals.VariableDescription(0, 130, "this", "Lart/Target;", null, 0),
+ new Locals.VariableDescription(85,
+ 45,
+ "i",
+ "Ljava/util/ArrayList;",
+ "Ljava/util/ArrayList<Ljava/lang/Integer;>;",
+ 4))),
+ // ART Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(19, 31, "q", "F", null, 6),
+ new Locals.VariableDescription(55, 37, "o", "Ljava/lang/Object;", null, 3),
+ new Locals.VariableDescription(0, 92, "this", "Lart/Target;", null, 14),
+ new Locals.VariableDescription(12, 80, "z", "I", null, 8),
+ new Locals.VariableDescription(11, 81, "y", "I", null, 5),
+ new Locals.VariableDescription(62, 30, "p", "I", null, 4),
+ new Locals.VariableDescription(0, 92, "x", "I", null, 15),
+ new Locals.VariableDescription(27, 23, "i", "D", null, 0),
+ new Locals.VariableDescription(65, 27, "q", "J", null, 6),
+ new Locals.VariableDescription(60,
+ 32,
+ "i",
+ "Ljava/util/ArrayList;",
+ "Ljava/util/ArrayList<Ljava/lang/Integer;>;",
+ 2))),
+ };
+
+ // Get a classloader that can load the Target class.
+ public static ClassLoader getClassLoader() throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ // We are on art since we got the InMemoryDexClassLoader.
+ return (ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test1911.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI.
+ return new ClassLoader(Test1911.class.getClassLoader()) {
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("art.Target")) {
+ return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+ } else {
+ return super.findClass(name);
+ }
+ }
+ };
+ }
+ }
+
+ public static void CheckLocalVariableTable(Executable m,
+ Set<Locals.VariableDescription>[] possible_vars) {
+ Set<Locals.VariableDescription> real_vars =
+ new HashSet<>(Arrays.asList(Locals.GetLocalVariableTable(m)));
+ for (Set<Locals.VariableDescription> pos : possible_vars) {
+ if (pos.equals(real_vars)) {
+ return;
+ }
+ }
+ System.out.println("Unexpected variables for " + m);
+ System.out.println("Received: " + real_vars);
+ System.out.println("Expected one of:");
+ for (Object pos : possible_vars) {
+ System.out.println("\t" + pos);
+ }
+ }
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ Class<?> target = getClassLoader().loadClass("art.Target");
+ CheckLocalVariableTable(target.getDeclaredConstructor(Integer.TYPE),
+ CONSTRUCTOR_VARIABLES);
+ CheckLocalVariableTable(target.getDeclaredMethod("doNothing", (new Object[0]).getClass()),
+ DO_NOTHING_VARIABLES);
+ CheckLocalVariableTable(target.getDeclaredMethod("doSomething", Integer.TYPE),
+ DO_SOMETHING_VARIABLES);
+ }
+}
+
diff --git a/test/1912-get-set-local-primitive/expected.txt b/test/1912-get-set-local-primitive/expected.txt
new file mode 100644
index 0000000000..f2c5ce8640
--- /dev/null
+++ b/test/1912-get-set-local-primitive/expected.txt
@@ -0,0 +1,108 @@
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.IntMethod(java.lang.Runnable) got value: 42
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.IntMethod(java.lang.Runnable) set value: 2147483647
+ Value is '2147483647' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed to set value 9223372036854775807 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed to set value 9.2 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed to set value 12.4 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.LongMethod(java.lang.Runnable) got value: 9001
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed to set value 2147483647 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.LongMethod(java.lang.Runnable) set value: 9223372036854775807
+ Value is '9223372036854775807' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed to set value 9.2 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed to set value 12.4 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.FloatMethod(java.lang.Runnable) got value: 1.618
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed to set value 2147483647 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed to set value 9223372036854775807 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.FloatMethod(java.lang.Runnable) set value: 9.2
+ Value is '9.2' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed to set value 12.4 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) got value: 3.1415
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed to set value 2147483647 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed to set value 9223372036854775807 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed to set value 9.2 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) set value: 12.4
+ Value is '12.4' (class: class java.lang.Double)
+Running public static void art.Test1912.BooleanMethod(java.lang.Runnable) with "SetIntBoolSize" on remote thread.
+"SetIntBoolSize" on public static void art.Test1912.BooleanMethod(java.lang.Runnable) set value: 1
+ Value is 'true' (class: class java.lang.Boolean)
+Running public static void art.Test1912.ByteMethod(java.lang.Runnable) with "SetIntByteSize" on remote thread.
+"SetIntByteSize" on public static void art.Test1912.ByteMethod(java.lang.Runnable) set value: 126
+ Value is '126' (class: class java.lang.Byte)
+Running public static void art.Test1912.CharMethod(java.lang.Runnable) with "SetIntCharSize" on remote thread.
+"SetIntCharSize" on public static void art.Test1912.CharMethod(java.lang.Runnable) set value: 65534
+ Value is '<Char: -1>' (class: class java.lang.String)
+Running public static void art.Test1912.ShortMethod(java.lang.Runnable) with "SetIntShortSize" on remote thread.
+"SetIntShortSize" on public static void art.Test1912.ShortMethod(java.lang.Runnable) set value: 32766
+ Value is '32766' (class: class java.lang.Short)
diff --git a/test/1912-get-set-local-primitive/info.txt b/test/1912-get-set-local-primitive/info.txt
new file mode 100644
index 0000000000..87a7b35333
--- /dev/null
+++ b/test/1912-get-set-local-primitive/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable primitives.
+
diff --git a/test/1912-get-set-local-primitive/run b/test/1912-get-set-local-primitive/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1912-get-set-local-primitive/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1912-get-set-local-primitive/src/Main.java b/test/1912-get-set-local-primitive/src/Main.java
new file mode 100644
index 0000000000..9ff69f8a05
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1912.run();
+ }
+}
diff --git a/test/1912-get-set-local-primitive/src/art/Breakpoint.java b/test/1912-get-set-local-primitive/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1912-get-set-local-primitive/src/art/Locals.java b/test/1912-get-set-local-primitive/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1912-get-set-local-primitive/src/art/StackTrace.java b/test/1912-get-set-local-primitive/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1912-get-set-local-primitive/src/art/Suspension.java b/test/1912-get-set-local-primitive/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1912-get-set-local-primitive/src/art/Test1912.java b/test/1912-get-set-local-primitive/src/art/Test1912.java
new file mode 100644
index 0000000000..24149f4cbe
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/art/Test1912.java
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+// TODO Rename test to set-get-local-prim
+
+public class Test1912 {
+ public static final String TARGET_VAR = "TARGET";
+
+
+ public static void reportValue(Object val) {
+ if (val instanceof Character) {
+ val = "<Char: " + Character.getNumericValue(((Character)val).charValue()) + ">";
+ }
+ System.out.println("\tValue is '" + val + "' (class: " + val.getClass() + ")");
+ }
+
+ public static void BooleanMethod(Runnable safepoint) {
+ boolean TARGET = false;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void ByteMethod(Runnable safepoint) {
+ byte TARGET = 8;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void CharMethod(Runnable safepoint) {
+ char TARGET = 'q';
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void ShortMethod(Runnable safepoint) {
+ short TARGET = 321;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void IntMethod(Runnable safepoint) {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void LongMethod(Runnable safepoint) {
+ long TARGET = 9001;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void FloatMethod(Runnable safepoint) {
+ float TARGET = 1.618f;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void DoubleMethod(Runnable safepoint) {
+ double TARGET = 3.1415d;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static interface SafepointFunction {
+ public void invoke(
+ Thread thread,
+ Method target,
+ Locals.VariableDescription TARGET_desc,
+ int depth) throws Exception;
+ }
+
+ public static interface SetterFunction {
+ public void SetVar(Thread t, int depth, int slot, Object v);
+ }
+
+ public static interface GetterFunction {
+ public Object GetVar(Thread t, int depth, int slot);
+ }
+
+ public static SafepointFunction NamedSet(
+ final String type, final SetterFunction get, final Object v) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ get.SetVar(t, depth, desc.slot, v);
+ System.out.println(this + " on " + method + " set value: " + v);
+ } catch (Exception e) {
+ System.out.println(
+ this + " on " + method + " failed to set value " + v + " due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Set" + type + "\"";
+ }
+ };
+ }
+
+ public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ Object res = get.GetVar(t, depth, desc.slot);
+ System.out.println(this + " on " + method + " got value: " + res);
+ } catch (Exception e) {
+ System.out.println(this + " on " + method + " failed due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Get" + type + "\"";
+ }
+ };
+ }
+
+ public static class TestCase {
+ public final Method target;
+
+ public TestCase(Method target) {
+ this.target = target;
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public void exec(final SafepointFunction safepoint) throws Exception {
+ System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
+ final ThreadPauser pause = new ThreadPauser();
+ Thread remote = new Thread(
+ () -> {
+ try {
+ target.invoke(null, pause);
+ } catch (Exception e) {
+ throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
+ }
+ },
+ "remote thread for " + target + " with " + safepoint);
+ remote.start();
+ pause.waitForOtherThreadToPause();
+ try {
+ Suspension.suspend(remote);
+ StackTrace.StackFrameData frame = findStackFrame(remote);
+ Locals.VariableDescription desc = findTargetVar(frame.current_location);
+ safepoint.invoke(remote, target, desc, frame.depth);
+ } finally {
+ Suspension.resume(remote);
+ pause.wakeupOtherThread();
+ remote.join();
+ }
+ }
+
+ private Locals.VariableDescription findTargetVar(long loc) {
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
+ }
+
+ private StackTrace.StackFrameData findStackFrame(Thread thr) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+ }
+ public static Method getMethod(String name) throws Exception {
+ return Test1912.class.getDeclaredMethod(name, Runnable.class);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+ new TestCase(getMethod("IntMethod")),
+ new TestCase(getMethod("LongMethod")),
+ new TestCase(getMethod("FloatMethod")),
+ new TestCase(getMethod("DoubleMethod")),
+ };
+
+ final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
+ NamedGet("Int", Locals::GetLocalVariableInt),
+ NamedGet("Long", Locals::GetLocalVariableLong),
+ NamedGet("Float", Locals::GetLocalVariableFloat),
+ NamedGet("Double", Locals::GetLocalVariableDouble),
+ NamedSet("Int", Locals::SetLocalVariableInt, Integer.MAX_VALUE),
+ NamedSet("Long", Locals::SetLocalVariableLong, Long.MAX_VALUE),
+ NamedSet("Float", Locals::SetLocalVariableFloat, 9.2f),
+ NamedSet("Double", Locals::SetLocalVariableDouble, 12.4d),
+ };
+
+ for (TestCase t: MAIN_TEST_CASES) {
+ for (SafepointFunction s : SAFEPOINTS) {
+ t.exec(s);
+ }
+ }
+
+ // Test int for small values.
+ new TestCase(getMethod("BooleanMethod")).exec(
+ NamedSet("IntBoolSize", Locals::SetLocalVariableInt, 1));
+ new TestCase(getMethod("ByteMethod")).exec(
+ NamedSet("IntByteSize", Locals::SetLocalVariableInt, Byte.MAX_VALUE - 1));
+
+ new TestCase(getMethod("CharMethod")).exec(
+ NamedSet("IntCharSize", Locals::SetLocalVariableInt, Character.MAX_VALUE - 1));
+ new TestCase(getMethod("ShortMethod")).exec(
+ NamedSet("IntShortSize", Locals::SetLocalVariableInt, Short.MAX_VALUE - 1));
+ }
+}
+
diff --git a/test/1913-get-set-local-objects/expected.txt b/test/1913-get-set-local-objects/expected.txt
new file mode 100644
index 0000000000..23f49920c9
--- /dev/null
+++ b/test/1913-get-set-local-objects/expected.txt
@@ -0,0 +1,72 @@
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) got value: TestClass1("ObjectMethod")
+ Value is 'TestClass1("ObjectMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: null
+ Value is 'null' (class: NULL)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass1("Set TestClass1")
+ Value is 'TestClass1("Set TestClass1")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass1ext("TestClass1("Set TestClass1ext")")
+ Value is 'TestClass1ext("TestClass1("Set TestClass1ext")")' (class: class art.Test1913$TestClass1ext)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass2("Set TestClass2")
+ Value is 'TestClass2("Set TestClass2")' (class: class art.Test1913$TestClass2)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass2impl("TestClass2("Set TestClass2impl")")
+ Value is 'TestClass2impl("TestClass2("Set TestClass2impl")")' (class: class art.Test1913$TestClass2impl)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) got value: TestClass1("InterfaceMethod")
+ Value is 'TestClass1("InterfaceMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: null
+ Value is 'null' (class: NULL)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: TestClass1("Set TestClass1")
+ Value is 'TestClass1("Set TestClass1")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: TestClass1ext("TestClass1("Set TestClass1ext")")
+ Value is 'TestClass1ext("TestClass1("Set TestClass1ext")")' (class: class art.Test1913$TestClass1ext)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) failed to set value TestClass2("Set TestClass2") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is 'TestClass1("InterfaceMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: TestClass2impl("TestClass2("Set TestClass2impl")")
+ Value is 'TestClass2impl("TestClass2("Set TestClass2impl")")' (class: class art.Test1913$TestClass2impl)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) got value: TestClass1("SpecificClassMethod")
+ Value is 'TestClass1("SpecificClassMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) set value: null
+ Value is 'null' (class: NULL)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) set value: TestClass1("Set TestClass1")
+ Value is 'TestClass1("Set TestClass1")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) set value: TestClass1ext("TestClass1("Set TestClass1ext")")
+ Value is 'TestClass1ext("TestClass1("Set TestClass1ext")")' (class: class art.Test1913$TestClass1ext)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) failed to set value TestClass2("Set TestClass2") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is 'TestClass1("SpecificClassMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) failed to set value TestClass2impl("TestClass2("Set TestClass2impl")") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is 'TestClass1("SpecificClassMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value null due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass1("Set TestClass1") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass1ext("TestClass1("Set TestClass1ext")") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass2("Set TestClass2") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass2impl("TestClass2("Set TestClass2impl")") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
diff --git a/test/1913-get-set-local-objects/info.txt b/test/1913-get-set-local-objects/info.txt
new file mode 100644
index 0000000000..86ac7435ec
--- /dev/null
+++ b/test/1913-get-set-local-objects/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get and set local variable object.
+
diff --git a/test/1913-get-set-local-objects/run b/test/1913-get-set-local-objects/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1913-get-set-local-objects/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1913-get-set-local-objects/src/Main.java b/test/1913-get-set-local-objects/src/Main.java
new file mode 100644
index 0000000000..45565c2897
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1913.run();
+ }
+}
diff --git a/test/1913-get-set-local-objects/src/art/Breakpoint.java b/test/1913-get-set-local-objects/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1913-get-set-local-objects/src/art/Locals.java b/test/1913-get-set-local-objects/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1913-get-set-local-objects/src/art/StackTrace.java b/test/1913-get-set-local-objects/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1913-get-set-local-objects/src/art/Suspension.java b/test/1913-get-set-local-objects/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1913-get-set-local-objects/src/art/Test1913.java b/test/1913-get-set-local-objects/src/art/Test1913.java
new file mode 100644
index 0000000000..417138a194
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/art/Test1913.java
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1913 {
+ public static final String TARGET_VAR = "TARGET";
+
+ public static interface TestInterface {
+ public default void doNothing() {}
+ }
+ public static class TestClass1 implements TestInterface {
+ public String id;
+ public TestClass1(String id) { this.id = id; }
+ public String toString() { return String.format("TestClass1(\"%s\")", id); }
+ }
+
+ public static class TestClass1ext extends TestClass1 {
+ public TestClass1ext(String id) { super(id); }
+ public String toString() { return String.format("TestClass1ext(\"%s\")", super.toString()); }
+ }
+ public static class TestClass2 {
+ public String id;
+ public TestClass2(String id) { this.id = id; }
+ public String toString() { return String.format("TestClass2(\"%s\")", id); }
+ }
+ public static class TestClass2impl extends TestClass2 implements TestInterface {
+ public TestClass2impl(String id) { super(id); }
+ public String toString() { return String.format("TestClass2impl(\"%s\")", super.toString()); }
+ }
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "' (class: "
+ + (val != null ? val.getClass() : "NULL") + ")");
+ }
+
+ public static void PrimitiveMethod(Runnable safepoint) {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ // b/64115302: Needed to make sure that DX doesn't change the type of TARGET to TestClass1.
+ private static Object AsObject(Object o) { return o; }
+ public static void ObjectMethod(Runnable safepoint) {
+ Object TARGET = AsObject(new TestClass1("ObjectMethod"));
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void InterfaceMethod(Runnable safepoint) {
+ TestInterface TARGET = new TestClass1("InterfaceMethod");
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void SpecificClassMethod(Runnable safepoint) {
+ TestClass1 TARGET = new TestClass1("SpecificClassMethod");
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static interface SafepointFunction {
+ public void invoke(
+ Thread thread,
+ Method target,
+ Locals.VariableDescription TARGET_desc,
+ int depth) throws Exception;
+ }
+
+ public static interface SetterFunction {
+ public void SetVar(Thread t, int depth, int slot, Object v);
+ }
+
+ public static interface GetterFunction {
+ public Object GetVar(Thread t, int depth, int slot);
+ }
+
+ public static SafepointFunction NamedSet(
+ final String type, final SetterFunction get, final Object v) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ get.SetVar(t, depth, desc.slot, v);
+ System.out.println(this + " on " + method + " set value: " + v);
+ } catch (Exception e) {
+ System.out.println(
+ this + " on " + method + " failed to set value " + v + " due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Set" + type + "\"";
+ }
+ };
+ }
+
+ public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ Object res = get.GetVar(t, depth, desc.slot);
+ System.out.println(this + " on " + method + " got value: " + res);
+ } catch (Exception e) {
+ System.out.println(this + " on " + method + " failed due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Get" + type + "\"";
+ }
+ };
+ }
+
+ public static class TestCase {
+ public final Method target;
+
+ public TestCase(Method target) {
+ this.target = target;
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public void exec(final SafepointFunction safepoint) throws Exception {
+ System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
+ final ThreadPauser pause = new ThreadPauser();
+ Thread remote = new Thread(
+ () -> {
+ try {
+ target.invoke(null, pause);
+ } catch (Exception e) {
+ throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
+ }
+ },
+ "remote thread for " + target + " with " + safepoint);
+ remote.start();
+ pause.waitForOtherThreadToPause();
+ try {
+ Suspension.suspend(remote);
+ StackTrace.StackFrameData frame = findStackFrame(remote);
+ Locals.VariableDescription desc = findTargetVar(frame.current_location);
+ safepoint.invoke(remote, target, desc, frame.depth);
+ } finally {
+ Suspension.resume(remote);
+ pause.wakeupOtherThread();
+ remote.join();
+ }
+ }
+
+ private Locals.VariableDescription findTargetVar(long loc) {
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
+ }
+
+ private StackTrace.StackFrameData findStackFrame(Thread thr) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+ }
+ public static Method getMethod(String name) throws Exception {
+ return Test1913.class.getDeclaredMethod(name, Runnable.class);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+ new TestCase(getMethod("ObjectMethod")),
+ new TestCase(getMethod("InterfaceMethod")),
+ new TestCase(getMethod("SpecificClassMethod")),
+ new TestCase(getMethod("PrimitiveMethod")),
+ };
+
+ final SetterFunction set_obj = Locals::SetLocalVariableObject;
+ final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
+ NamedGet("GetObject", Locals::GetLocalVariableObject),
+ NamedSet("Null", set_obj, null),
+ NamedSet("TestClass1", set_obj, new TestClass1("Set TestClass1")),
+ NamedSet("TestClass1ext", set_obj, new TestClass1ext("Set TestClass1ext")),
+ NamedSet("TestClass2", set_obj, new TestClass2("Set TestClass2")),
+ NamedSet("TestClass2impl", set_obj, new TestClass2impl("Set TestClass2impl")),
+ };
+
+ for (TestCase t: MAIN_TEST_CASES) {
+ for (SafepointFunction s : SAFEPOINTS) {
+ t.exec(s);
+ }
+ }
+ }
+}
+
diff --git a/test/1914-get-local-instance/expected.txt b/test/1914-get-local-instance/expected.txt
new file mode 100644
index 0000000000..4117942392
--- /dev/null
+++ b/test/1914-get-local-instance/expected.txt
@@ -0,0 +1,12 @@
+Running public static void art.Test1914.StaticMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public static void art.Test1914.StaticMethod(java.lang.Runnable) got value: null
+ Value is 'null' (class: NULL)
+Running public static native void art.Test1914.NativeStaticMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public static native void art.Test1914.NativeStaticMethod(java.lang.Runnable) got value: null
+ Value is 'null' (class: NULL)
+Running public void art.Test1914$TargetClass.InstanceMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public void art.Test1914$TargetClass.InstanceMethod(java.lang.Runnable) got value: TargetClass("InstanceMethodObject")
+ Value is 'TargetClass("InstanceMethodObject")' (class: class art.Test1914$TargetClass)
+Running public native void art.Test1914$TargetClass.NativeInstanceMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public native void art.Test1914$TargetClass.NativeInstanceMethod(java.lang.Runnable) got value: TargetClass("NativeInstanceMethodObject")
+ Value is 'TargetClass("NativeInstanceMethodObject")' (class: class art.Test1914$TargetClass)
diff --git a/test/1914-get-local-instance/info.txt b/test/1914-get-local-instance/info.txt
new file mode 100644
index 0000000000..9fc3d62cd6
--- /dev/null
+++ b/test/1914-get-local-instance/info.txt
@@ -0,0 +1,2 @@
+Test for jvmti get local instance
+
diff --git a/test/1914-get-local-instance/local_instance.cc b/test/1914-get-local-instance/local_instance.cc
new file mode 100644
index 0000000000..03aa59e484
--- /dev/null
+++ b/test/1914-get-local-instance/local_instance.cc
@@ -0,0 +1,68 @@
+/*
+ * 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 <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1914LocalInstance {
+
+extern "C" JNIEXPORT void Java_art_Test1914_00024TargetClass_NativeInstanceMethod(
+ JNIEnv* env, jobject thiz, jobject run) {
+ ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallVoidMethod(run, method);
+ if (env->ExceptionCheck()) { return; }
+ ScopedLocalRef<jclass> Test1914(env, env->FindClass("art/Test1914"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID report = env->GetStaticMethodID(Test1914.get(), "reportValue", "(Ljava/lang/Object;)V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallStaticVoidMethod(Test1914.get(), report, thiz);
+}
+
+extern "C" JNIEXPORT void Java_art_Test1914_NativeStaticMethod(
+ JNIEnv* env, jclass, jobject run) {
+ ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallVoidMethod(run, method);
+ if (env->ExceptionCheck()) { return; }
+ ScopedLocalRef<jclass> Test1914(env, env->FindClass("art/Test1914"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID report = env->GetStaticMethodID(Test1914.get(), "reportValue", "(Ljava/lang/Object;)V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallStaticVoidMethod(Test1914.get(), report, nullptr);
+}
+
+} // namespace Test1914LocalInstance
+} // namespace art
+
diff --git a/test/1914-get-local-instance/run b/test/1914-get-local-instance/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1914-get-local-instance/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1914-get-local-instance/src/Main.java b/test/1914-get-local-instance/src/Main.java
new file mode 100644
index 0000000000..163221e12f
--- /dev/null
+++ b/test/1914-get-local-instance/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1914.run();
+ }
+}
diff --git a/test/1914-get-local-instance/src/art/Breakpoint.java b/test/1914-get-local-instance/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1914-get-local-instance/src/art/Locals.java b/test/1914-get-local-instance/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1914-get-local-instance/src/art/StackTrace.java b/test/1914-get-local-instance/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1914-get-local-instance/src/art/Suspension.java b/test/1914-get-local-instance/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1914-get-local-instance/src/art/Test1914.java b/test/1914-get-local-instance/src/art/Test1914.java
new file mode 100644
index 0000000000..c09f519db8
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/Test1914.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1914 {
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "' (class: "
+ + (val != null ? val.getClass() : "NULL") + ")");
+ }
+
+ public static void StaticMethod(Runnable safepoint) {
+ safepoint.run();
+ reportValue(null);
+ }
+
+ public static native void NativeStaticMethod(Runnable safepoint);
+
+ public static class TargetClass {
+ public String id;
+ public String toString() { return String.format("TargetClass(\"%s\")", id); }
+ public TargetClass(String id) { this.id = id; }
+
+ public void InstanceMethod(Runnable safepoint) {
+ safepoint.run();
+ reportValue(this);
+ }
+
+ public native void NativeInstanceMethod(Runnable safepoint);
+ }
+
+ public static interface SafepointFunction {
+ public void invoke(
+ Thread thread,
+ Method target,
+ int depth) throws Exception;
+ }
+
+ public static interface GetterFunction {
+ public Object GetVar(Thread t, int depth);
+ }
+
+ public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, int depth) {
+ try {
+ Object res = get.GetVar(t, depth);
+ System.out.println(this + " on " + method + " got value: " + res);
+ } catch (Exception e) {
+ System.out.println(this + " on " + method + " failed due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Get" + type + "\"";
+ }
+ };
+ }
+
+ public static class TestCase {
+ public final Object thiz;
+ public final Method target;
+
+ public TestCase(Method target) {
+ this(null, target);
+ }
+ public TestCase(Object thiz, Method target) {
+ this.thiz = thiz;
+ this.target = target;
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public void exec(final SafepointFunction safepoint) throws Exception {
+ System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
+ final ThreadPauser pause = new ThreadPauser();
+ Thread remote = new Thread(
+ () -> {
+ try {
+ target.invoke(thiz, pause);
+ } catch (Exception e) {
+ throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
+ }
+ },
+ "remote thread for " + target + " with " + safepoint);
+ remote.start();
+ pause.waitForOtherThreadToPause();
+ try {
+ Suspension.suspend(remote);
+ StackTrace.StackFrameData frame = findStackFrame(remote);
+ safepoint.invoke(remote, target, frame.depth);
+ } finally {
+ Suspension.resume(remote);
+ pause.wakeupOtherThread();
+ remote.join();
+ }
+ }
+
+ private StackTrace.StackFrameData findStackFrame(Thread thr) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+ }
+
+ public static Method getMethod(Class<?> klass, String name) throws Exception {
+ return klass.getDeclaredMethod(name, Runnable.class);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+ new TestCase(null, getMethod(Test1914.class, "StaticMethod")),
+ new TestCase(null, getMethod(Test1914.class, "NativeStaticMethod")),
+ new TestCase(new TargetClass("InstanceMethodObject"),
+ getMethod(TargetClass.class, "InstanceMethod")),
+ new TestCase(new TargetClass("NativeInstanceMethodObject"),
+ getMethod(TargetClass.class, "NativeInstanceMethod")),
+ };
+
+ for (TestCase t: MAIN_TEST_CASES) {
+ t.exec(NamedGet("This", Locals::GetLocalInstance));
+ }
+ }
+}
+
diff --git a/test/1915-get-set-local-current-thread/expected.txt b/test/1915-get-set-local-current-thread/expected.txt
new file mode 100644
index 0000000000..de39ca9554
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/expected.txt
@@ -0,0 +1,5 @@
+GetLocalInt on current thread!
+From GetLocalInt(), value is 42
+ Value is '42'
+SetLocalInt on current thread!
+ Value is '1337'
diff --git a/test/1915-get-set-local-current-thread/info.txt b/test/1915-get-set-local-current-thread/info.txt
new file mode 100644
index 0000000000..c59f50de3b
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable on current thread.
+
diff --git a/test/1915-get-set-local-current-thread/run b/test/1915-get-set-local-current-thread/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1915-get-set-local-current-thread/src/Main.java b/test/1915-get-set-local-current-thread/src/Main.java
new file mode 100644
index 0000000000..47e676704f
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1915.run();
+ }
+}
diff --git a/test/1915-get-set-local-current-thread/src/art/Breakpoint.java b/test/1915-get-set-local-current-thread/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1915-get-set-local-current-thread/src/art/Locals.java b/test/1915-get-set-local-current-thread/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1915-get-set-local-current-thread/src/art/StackTrace.java b/test/1915-get-set-local-current-thread/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1915-get-set-local-current-thread/src/art/Suspension.java b/test/1915-get-set-local-current-thread/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1915-get-set-local-current-thread/src/art/Test1915.java b/test/1915-get-set-local-current-thread/src/art/Test1915.java
new file mode 100644
index 0000000000..a99a487ea6
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/art/Test1915.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1915 {
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
+ public static interface ThrowRunnable {
+ public void run() throws Exception;
+ }
+
+ public static void IntMethod(ThrowRunnable safepoint) throws Exception {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final Method target = Test1915.class.getDeclaredMethod("IntMethod", ThrowRunnable.class);
+ // Get Variable.
+ System.out.println("GetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(Thread.currentThread(), depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ });
+ // Set Variable.
+ System.out.println("SetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ Locals.SetLocalVariableInt(Thread.currentThread(), depth, slot, SET_VALUE);
+ });
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ public static int FindExpectedFrameDepth(StackTrace.StackFrameData frame) throws Exception {
+ // Adjust the 'frame' depth since it is modified by:
+ // +1 for Get/SetLocalVariableInt in future.
+ // -1 for FindStackFrame
+ // -1 for GetStackTrace
+ // -1 for GetStackTraceNative
+ // ------------------------------
+ // -2
+ return frame.depth - 2;
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target);
+ }
+}
+
diff --git a/test/1916-get-set-current-frame/expected.txt b/test/1916-get-set-current-frame/expected.txt
new file mode 100644
index 0000000000..343d49310e
--- /dev/null
+++ b/test/1916-get-set-current-frame/expected.txt
@@ -0,0 +1,4 @@
+From GetLocalInt(), value is 42
+ Value is '42'
+Setting TARGET to 1337
+ Value is '1337'
diff --git a/test/1916-get-set-current-frame/info.txt b/test/1916-get-set-current-frame/info.txt
new file mode 100644
index 0000000000..7342af7e71
--- /dev/null
+++ b/test/1916-get-set-current-frame/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable in the currently executing method frame.
+
diff --git a/test/1916-get-set-current-frame/run b/test/1916-get-set-current-frame/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1916-get-set-current-frame/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1916-get-set-current-frame/src/Main.java b/test/1916-get-set-current-frame/src/Main.java
new file mode 100644
index 0000000000..7d0cd21772
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1916.run();
+ }
+}
diff --git a/test/1916-get-set-current-frame/src/art/Breakpoint.java b/test/1916-get-set-current-frame/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1916-get-set-current-frame/src/art/Locals.java b/test/1916-get-set-current-frame/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1916-get-set-current-frame/src/art/StackTrace.java b/test/1916-get-set-current-frame/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1916-get-set-current-frame/src/art/Suspension.java b/test/1916-get-set-current-frame/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1916-get-set-current-frame/src/art/Test1916.java b/test/1916-get-set-current-frame/src/art/Test1916.java
new file mode 100644
index 0000000000..3e5bce2619
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/art/Test1916.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1916 {
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
+
+ public static class IntRunner implements Runnable {
+ private volatile boolean continueBusyLoop;
+ private volatile boolean inBusyLoop;
+ public IntRunner() {
+ this.continueBusyLoop = true;
+ this.inBusyLoop = false;
+ }
+ public void run() {
+ int TARGET = 42;
+ // We will suspend the thread during this loop.
+ while (continueBusyLoop) {
+ inBusyLoop = true;
+ }
+ reportValue(TARGET);
+ }
+ public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+ public void finish() { continueBusyLoop = false; }
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ runGet();
+ runSet();
+ }
+
+ public static void runGet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Get Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+ target_get.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_get);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_get.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ } finally {
+ Suspension.resume(target_get);
+ int_runner.finish();
+ target_get.join();
+ }
+ }
+
+ public static void runSet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Set Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+ target_set.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_set);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_set.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ System.out.println("Setting TARGET to " + SET_VALUE);
+ Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+ } finally {
+ Suspension.resume(target_set);
+ int_runner.finish();
+ target_set.join();
+ }
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+}
+
diff --git a/test/1917-get-stack-frame/expected.txt b/test/1917-get-stack-frame/expected.txt
new file mode 100644
index 0000000000..4c9efcf157
--- /dev/null
+++ b/test/1917-get-stack-frame/expected.txt
@@ -0,0 +1,33 @@
+Recurring 5 times
+'private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread)' line: -1
+'public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread)' line: 60
+'public void art.Test1917$StackTraceGenerator.run()' line: 82
+'public void art.Test1917$RecurCount.doRecur(int)' line: 104
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.run()' line: 97
+'public static void art.Test1917.run() throws java.lang.Exception' line: 133
+Recurring 5 times on another thread
+'private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread)' line: -1
+'public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread)' line: 60
+'public void art.Test1917$StackTraceGenerator.run()' line: 82
+'public void art.Test1917$RecurCount.doRecur(int)' line: 104
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.run()' line: 97
+Recurring 5 times on another thread. Stack trace from main thread!
+'public void java.util.concurrent.Semaphore.acquire() throws java.lang.InterruptedException' line: <NOT-DETERMINISTIC>
+'public void art.Test1917$ThreadPauser.run()' line: 46
+'public void art.Test1917$RecurCount.doRecur(int)' line: 104
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.run()' line: 97
diff --git a/test/1917-get-stack-frame/info.txt b/test/1917-get-stack-frame/info.txt
new file mode 100644
index 0000000000..e72034adef
--- /dev/null
+++ b/test/1917-get-stack-frame/info.txt
@@ -0,0 +1 @@
+Tests stack frame functions of jvmti
diff --git a/test/1917-get-stack-frame/run b/test/1917-get-stack-frame/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1917-get-stack-frame/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1917-get-stack-frame/src/Main.java b/test/1917-get-stack-frame/src/Main.java
new file mode 100644
index 0000000000..c055a5c540
--- /dev/null
+++ b/test/1917-get-stack-frame/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1917.run();
+ }
+}
diff --git a/test/1917-get-stack-frame/src/art/Breakpoint.java b/test/1917-get-stack-frame/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1917-get-stack-frame/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1917-get-stack-frame/src/art/StackTrace.java b/test/1917-get-stack-frame/src/art/StackTrace.java
new file mode 100644
index 0000000000..b12c3df66b
--- /dev/null
+++ b/test/1917-get-stack-frame/src/art/StackTrace.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread());
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1917-get-stack-frame/src/art/Suspension.java b/test/1917-get-stack-frame/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1917-get-stack-frame/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1917-get-stack-frame/src/art/Test1917.java b/test/1917-get-stack-frame/src/art/Test1917.java
new file mode 100644
index 0000000000..def7530bff
--- /dev/null
+++ b/test/1917-get-stack-frame/src/art/Test1917.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.Vector;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1917 {
+ public final static boolean TEST_PRINT_ALL = false;
+
+ public static class ThreadPauser implements Runnable {
+ public Semaphore sem_wakeup_main = new Semaphore(0);
+ public Semaphore sem_wait = new Semaphore(0);
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ while (!sem_wait.hasQueuedThreads()) {}
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public static class StackTraceGenerator implements Runnable {
+ private final Thread thr;
+ private final Consumer<StackTrace.StackFrameData> con;
+ public StackTraceGenerator(Thread thr, Consumer<StackTrace.StackFrameData> con) {
+ this.thr = thr;
+ this.con = con;
+ }
+
+ public StackTraceGenerator(Consumer<StackTrace.StackFrameData> con) {
+ this(null, con);
+ }
+
+ public Thread getThread() {
+ if (thr == null) {
+ return Thread.currentThread();
+ } else {
+ return thr;
+ }
+ }
+ public void run() {
+ for (StackTrace.StackFrameData s : StackTrace.GetStackTrace(getThread())) {
+ con.accept(s);
+ }
+ }
+ }
+
+ public static class RecurCount implements Runnable {
+ private final int cnt;
+ private final Runnable then;
+ public RecurCount(int cnt, Runnable then) {
+ this.cnt = cnt;
+ this.then = then;
+ }
+
+ public void run() {
+ doRecur(0);
+ }
+
+ public void doRecur(int n) {
+ if (n < cnt) {
+ doRecur(n + 1);
+ } else {
+ then.run();
+ }
+ }
+ }
+
+ public static Consumer<StackTrace.StackFrameData> makePrintStackFramesConsumer()
+ throws Exception {
+ final Method end_method = Test1917.class.getDeclaredMethod("run");
+ return new Consumer<StackTrace.StackFrameData>() {
+ public void accept(StackTrace.StackFrameData data) {
+ if (TEST_PRINT_ALL) {
+ System.out.println(data);
+ } else {
+ Package p = data.method.getDeclaringClass().getPackage();
+ // Filter out anything to do with the testing harness.
+ if (p != null && p.equals(Test1917.class.getPackage())) {
+ System.out.printf("'%s' line: %d\n",
+ data.method,
+ Breakpoint.locationToLine(data.method, data.current_location));
+ } else if (data.method.getDeclaringClass().equals(Semaphore.class)) {
+ System.out.printf("'%s' line: <NOT-DETERMINISTIC>\n", data.method);
+ }
+ }
+ }
+ };
+ }
+
+ public static void run() throws Exception {
+ System.out.println("Recurring 5 times");
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())).run();
+
+ System.out.println("Recurring 5 times on another thread");
+ Thread thr = new Thread(
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())));
+ thr.start();
+ thr.join();
+
+ System.out.println("Recurring 5 times on another thread. Stack trace from main thread!");
+ ThreadPauser pause = new ThreadPauser();
+ Thread thr2 = new Thread(new RecurCount(5, pause));
+ thr2.start();
+ pause.waitForOtherThreadToPause();
+ new StackTraceGenerator(thr2, makePrintStackFramesConsumer()).run();
+ pause.wakeupOtherThread();
+ thr2.join();
+ }
+}
diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java
index 810f0faaa6..6fd9cdd010 100644
--- a/test/550-checker-multiply-accumulate/src/Main.java
+++ b/test/550-checker-multiply-accumulate/src/Main.java
@@ -434,7 +434,7 @@ public class Main {
/// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<<Loop>> outer_loop:none
/// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: VecMull
+ /// CHECK-NOT: VecMul
/// CHECK-NOT: VecAdd
public static void SimdMulAdd(int[] array1, int[] array2) {
for (int j = 0; j < 100; j++) {
@@ -452,7 +452,7 @@ public class Main {
/// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<<Loop>> outer_loop:none
/// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: VecMull
+ /// CHECK-NOT: VecMul
/// CHECK-NOT: VecSub
public static void SimdMulSub(int[] array1, int[] array2) {
for (int j = 0; j < 100; j++) {
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index beea90a57d..27da7ccec2 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -187,7 +187,12 @@ public class Main {
System.loadLibrary(args[0]);
// CHeck some boot-image methods.
- assertSingleImplementation(java.util.ArrayList.class, "size", true);
+
+ // We would want to have this, but currently setting single-implementation in the boot image
+ // does not work well with app images. b/34193647
+ final boolean ARRAYLIST_SIZE_EXPECTED = false;
+ assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
+
// java.util.LinkedHashMap overrides get().
assertSingleImplementation(java.util.HashMap.class, "get", false);
diff --git a/test/Android.bp b/test/Android.bp
index 44cb4f6e8a..fab664a3e2 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -252,8 +252,10 @@ art_cc_defaults {
"ti-agent/test_env.cc",
"ti-agent/breakpoint_helper.cc",
"ti-agent/common_helper.cc",
+ "ti-agent/locals_helper.cc",
"ti-agent/redefinition_helper.cc",
"ti-agent/suspension_helper.cc",
+ "ti-agent/stack_trace_helper.cc",
"ti-agent/trace_helper.cc",
// This is the list of non-special OnLoad things and excludes BCI and anything that depends
// on ART internals.
@@ -292,6 +294,7 @@ art_cc_defaults {
"1905-suspend-native/native_suspend.cc",
"1908-suspend-native-resume-self/native_suspend_resume.cc",
"1909-per-agent-tls/agent_tls.cc",
+ "1914-get-local-instance/local_instance.cc",
],
shared_libs: [
"libbase",
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index ede485a81b..e989e39bf3 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -453,6 +453,10 @@ fi
if [ "$USE_JVM" = "y" ]; then
export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
+ # Some jvmti tests are flaky without -Xint on the RI.
+ if [ "$IS_JVMTI_TEST" = "y" ]; then
+ FLAGS="${FLAGS} -Xint"
+ fi
# Xmx is necessary since we don't pass down the ART flags to JVM.
# We pass the classes2 path whether it's used (src-multidex) or not.
cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}"
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 304b760bb0..5a67fbcc45 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -707,15 +707,9 @@
"variant": "gcstress & jit & target"
},
{
- "tests": ["004-JniTest"],
- "description": [ "Tests failing with --build-with-javac-dx since the new annotation",
- "lookup changes" ],
- "bug": "b/63089991",
- "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}
- },
- {
"tests": "660-clinit",
- "variant": "no-image | no-dex2oat",
- "description": "Tests <clinit> for app images, which --no-image and --no-dex2oat do not create"
+ "variant": "no-image | no-dex2oat | no-prebuild",
+ "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild and",
+ "--no-dex2oat do not create"]
}
]
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index 51d3406a35..7280102c6f 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -15,6 +15,7 @@
*/
#include "jvmti_helper.h"
+#include "test_env.h"
#include <dlfcn.h>
@@ -57,7 +58,7 @@ static const jvmtiCapabilities standard_caps = {
.can_get_line_numbers = 1,
.can_get_source_debug_extension = 1,
.can_access_local_variables = 0,
- .can_maintain_original_method_order = 0,
+ .can_maintain_original_method_order = 1,
.can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
.can_generate_frame_pop_events = 0,
@@ -90,6 +91,11 @@ jvmtiCapabilities GetStandardCapabilities() {
}
void SetStandardCapabilities(jvmtiEnv* env) {
+ if (IsJVM()) {
+ // RI is more strict about adding capabilities at runtime then ART so just give it everything.
+ SetAllCapabilities(env);
+ return;
+ }
jvmtiCapabilities caps = GetStandardCapabilities();
CheckJvmtiError(env, env->AddCapabilities(&caps));
}
@@ -100,7 +106,7 @@ void SetAllCapabilities(jvmtiEnv* env) {
CheckJvmtiError(env, env->AddCapabilities(&caps));
}
-bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error) {
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmtienv, jvmtiError error) {
if (error == JVMTI_ERROR_NONE) {
return false;
}
@@ -112,11 +118,11 @@ bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error) {
}
char* err;
- CheckJvmtiError(jvmti_env, jvmti_env->GetErrorName(error, &err));
+ CheckJvmtiError(jvmtienv, jvmtienv->GetErrorName(error, &err));
env->ThrowNew(rt_exception.get(), err);
- Deallocate(jvmti_env, err);
+ Deallocate(jvmtienv, err);
return true;
}
diff --git a/test/ti-agent/jvmti_helper.h b/test/ti-agent/jvmti_helper.h
index 78d238a980..a47a4028a9 100644
--- a/test/ti-agent/jvmti_helper.h
+++ b/test/ti-agent/jvmti_helper.h
@@ -43,7 +43,7 @@ void CheckJvmtiError(jvmtiEnv* env, jvmtiError error);
// Convert the given error to a RuntimeException with a message derived from the error. Returns
// true on error, false if error is JVMTI_ERROR_NONE.
-bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error);
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmtienv, jvmtiError error);
class JvmtiDeleter {
public:
diff --git a/test/ti-agent/locals_helper.cc b/test/ti-agent/locals_helper.cc
new file mode 100644
index 0000000000..e284b52846
--- /dev/null
+++ b/test/ti-agent/locals_helper.cc
@@ -0,0 +1,210 @@
+/*
+ * 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 "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace common_locals {
+
+static void DeallocateContents(jvmtiLocalVariableEntry* vars, jint nvars) {
+ for (jint i = 0; i < nvars; i++) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars[i].name));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars[i].signature));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars[i].generic_signature));
+ }
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_EnableLocalVariableAccess(JNIEnv* env, jclass) {
+ jvmtiCapabilities caps;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetCapabilities(&caps))) {
+ return;
+ }
+ caps.can_access_local_variables = 1;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableObject(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jobject val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalObject(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableDouble(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jdouble val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalDouble(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableFloat(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jfloat val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalFloat(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableLong(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jlong val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalLong(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableInt(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jint val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalInt(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT jdouble Java_art_Locals_GetLocalVariableDouble(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jdouble ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalDouble(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jfloat Java_art_Locals_GetLocalVariableFloat(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jfloat ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalFloat(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong Java_art_Locals_GetLocalVariableLong(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jlong ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalLong(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jint Java_art_Locals_GetLocalVariableInt(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jint ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalInt(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobject Java_art_Locals_GetLocalInstance(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth) {
+ jobject ret = nullptr;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalInstance(t, depth, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobject Java_art_Locals_GetLocalVariableObject(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jobject ret = nullptr;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalObject(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray Java_art_Locals_GetLocalVariableTable(JNIEnv* env,
+ jclass,
+ jobject m) {
+ jmethodID method = env->FromReflectedMethod(m);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Locals$VariableDescription"));
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jint nvars;
+ jvmtiLocalVariableEntry* vars = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetLocalVariableTable(method, &nvars, &vars))) {
+ return nullptr;
+ }
+ jobjectArray vars_array = env->NewObjectArray(nvars, klass.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return nullptr;
+ }
+
+ jmethodID constructor = env->GetMethodID(
+ klass.get(), "<init>", "(JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ for (jint i = 0; i < nvars; i++) {
+ ScopedLocalRef<jstring> name_string(env, env->NewStringUTF(vars[i].name));
+ ScopedLocalRef<jstring> sig_string(env, env->NewStringUTF(vars[i].signature));
+ ScopedLocalRef<jstring> generic_sig_string(env, env->NewStringUTF(vars[i].generic_signature));
+ jobject var_obj = env->NewObject(klass.get(),
+ constructor,
+ vars[i].start_location,
+ vars[i].length,
+ name_string.get(),
+ sig_string.get(),
+ generic_sig_string.get(),
+ vars[i].slot);
+ if (env->ExceptionCheck()) {
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return nullptr;
+ }
+ env->SetObjectArrayElement(vars_array, i, var_obj);
+ if (env->ExceptionCheck()) {
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return nullptr;
+ }
+ }
+
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return vars_array;
+}
+
+} // namespace common_locals
+} // namespace art
diff --git a/test/ti-agent/stack_trace_helper.cc b/test/ti-agent/stack_trace_helper.cc
new file mode 100644
index 0000000000..f2a8e9a318
--- /dev/null
+++ b/test/ti-agent/stack_trace_helper.cc
@@ -0,0 +1,99 @@
+
+/*
+ * 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 "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace common_stack_trace {
+
+extern "C" JNIEXPORT jint JNICALL Java_art_StackTrace_GetStackDepth(
+ JNIEnv* env, jclass, jthread thr) {
+ jint ret;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetFrameCount(thr, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray Java_art_StackTrace_nativeGetStackTrace(JNIEnv* env,
+ jclass,
+ jthread thr) {
+ jint depth;
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/StackTrace$StackFrameData"));
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jmethodID constructor = env->GetMethodID(
+ klass.get(), "<init>", "(Ljava/lang/Thread;Ljava/lang/reflect/Executable;JI)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetFrameCount(thr, &depth))) {
+ return nullptr;
+ }
+ // Just give some extra space.
+ depth += 10;
+ jvmtiFrameInfo* frames;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->Allocate(depth * sizeof(jvmtiFrameInfo),
+ reinterpret_cast<unsigned char**>(&frames)))) {
+ return nullptr;
+ }
+ jint nframes = 0;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetStackTrace(thr, 0, depth, frames, &nframes))) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ jobjectArray frames_array = env->NewObjectArray(nframes, klass.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ for (jint i = 0; i < nframes; i++) {
+ jobject jmethod = GetJavaMethod(jvmti_env, env, frames[i].method);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ jobject frame_obj = env->NewObject(klass.get(),
+ constructor,
+ thr,
+ jmethod,
+ frames[i].location,
+ i);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ env->SetObjectArrayElement(frames_array, i, frame_obj);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return frames_array;
+}
+
+} // namespace common_stack_trace
+} // namespace art
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
index a635fe9928..1f74262eb4 100644
--- a/tools/dexfuzz/README
+++ b/tools/dexfuzz/README
@@ -145,7 +145,7 @@ OppositeBranchChanger 40
PoolIndexChanger 30
RandomBranchChanger 30
RandomInstructionGenerator 30
-RegisterClobber 40
+RegisterClobber 10
SwitchBranchShifter 30
TryBlockShifter 40
ValuePrinter 40
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
index d37bd34016..2b3b8e7753 100644
--- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -33,9 +33,9 @@ import dexfuzz.listeners.UpdatingConsoleListener;
* Entrypoint class for dexfuzz.
*/
public class DexFuzz {
- // Last version update 1.5: added register clobber mutator.
+ // Last version update 1.7: changed the likelihood of RegisterClobber.
private static int majorVersion = 1;
- private static int minorVersion = 5;
+ private static int minorVersion = 7;
private static int seedChangeVersion = 0;
/**
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java
index aba7971b48..e640b4e2ef 100644
--- a/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java
@@ -28,8 +28,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-// This mutation might change the length of an array but can also change the
-// value of the register in every place it is used.
public class NewArrayLengthChanger extends CodeMutator {
/**
* Every CodeMutator has an AssociatedMutation, representing the
@@ -116,20 +114,46 @@ public class NewArrayLengthChanger extends CodeMutator {
MutatableCode mutatableCode = mutation.mutatableCode;
MInsn newArrayInsn = newArrayLengthInsns.get(mutation.newArrayToChangeIdx);
int newArrayInsnIdx = mutatableCode.getInstructionIndex(newArrayInsn);
+ // If the original new-array instruction is no longer present
+ // in the code (as indicated by a negative index), we make a
+ // best effort to find any other new-array instruction to
+ // apply the mutation to. If that effort fails, we simply
+ // bail by doing nothing.
+ if (newArrayInsnIdx < 0) {
+ newArrayInsnIdx = scanNewArray(mutatableCode);
+ if (newArrayInsnIdx == -1) {
+ return;
+ }
+ }
MInsn newInsn = new MInsn();
newInsn.insn = new Instruction();
newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_16);
+ mutatableCode.allocateTemporaryVRegs(1);
+ newArrayInsn.insn.vregB = mutatableCode.getTemporaryVReg(0);
newInsn.insn.vregA = (int) newArrayInsn.insn.vregB;
// New length chosen randomly between 1 to 100.
newInsn.insn.vregB = rng.nextInt(100);
mutatableCode.insertInstructionAt(newInsn, newArrayInsnIdx);
Log.info("Changed the length of the array to " + newInsn.insn.vregB);
stats.incrementStat("Changed length of new array");
+ mutatableCode.finishedUsingTemporaryVRegs();
}
private boolean isNewArray(MInsn mInsn) {
Opcode opcode = mInsn.insn.info.opcode;
return opcode == Opcode.NEW_ARRAY;
}
+
+ // Return the index of first new-array in the method, -1 otherwise.
+ private int scanNewArray(MutatableCode mutatableCode) {
+ int idx = 0;
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isNewArray(mInsn)) {
+ return idx;
+ }
+ idx++;
+ }
+ return -1;
+ }
} \ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java
index 11da1d4d39..90f4f0f089 100644
--- a/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java
@@ -61,7 +61,7 @@ public class RegisterClobber extends CodeMutator{
public RegisterClobber(Random rng, MutationStats stats, List<Mutation> mutations) {
super(rng, stats, mutations);
- likelihood = 40;
+ likelihood = 10;
}
@Override
@@ -90,6 +90,7 @@ public class RegisterClobber extends CodeMutator{
newInsn.insn = new Instruction();
newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_16);
newInsn.insn.vregA = i;
+ // Used zero because it may also apply to objects, resulting in fewer verification failures.
newInsn.insn.vregB = 0;
mutatableCode.insertInstructionAt(newInsn, mutation.regClobberIdx + i);
}
diff --git a/tools/generate_cmake_lists.py b/tools/generate_cmake_lists.py
new file mode 100755
index 0000000000..6c3ce085ee
--- /dev/null
+++ b/tools/generate_cmake_lists.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""
+./generate_cmake_lists.py --project-name <project-name> --arch <arch>
+
+- project-name - name of the new project
+- arch - arch type. make generates seperate CMakeLists files for
+ each architecture. To avoid collision in targets, only one of
+ them can be included in the super project.
+
+The primary objective of this file is to generate CMakeLists files
+for CLion setup.
+
+Steps to setup CLion.
+1) Open the generated CMakeList file in CLion as a project.
+2) Change the project root ANDROID_BUILD_TOP.
+(Also, exclude projects that you don't bother about. This will make
+the indexing faster).
+"""
+
+import sys
+import os
+import subprocess
+import argparse
+
+def get_android_build_top():
+ path_to_top = os.environ.get('ANDROID_BUILD_TOP')
+ if not path_to_top:
+ # nothing set. try to guess it based on the relative path of this env.py file.
+ this_file_path = os.path.realpath(__file__)
+ path_to_top = os.path.join(os.path.dirname(this_file_path), '../..')
+ path_to_top = os.path.realpath(path_to_top)
+
+ if not os.path.exists(os.path.join(path_to_top, 'build/envsetup.sh')):
+ print path_to_top
+ raise AssertionError("geneate_cmake_lists.py must be located inside an android source tree")
+
+ return path_to_top
+
+def main():
+ # Parse arguments
+ parser = argparse.ArgumentParser(description="Generate CMakeLists files for ART")
+ parser.add_argument('--project-name', dest="project_name", required=True,
+ help='name of the project')
+ parser.add_argument('--arch', dest="arch", required=True, help='arch')
+ args = parser.parse_args()
+ project_name = args.project_name
+ arch = args.arch
+
+ # Invoke make to generate CMakeFiles
+ os.environ['SOONG_GEN_CMAKEFILES']='1'
+ os.environ['SOONG_GEN_CMAKEFILES_DEBUG']='1'
+
+ ANDROID_BUILD_TOP = get_android_build_top()
+
+ subprocess.check_output(('make -j64 -C %s') % (ANDROID_BUILD_TOP), shell=True)
+
+ out_art_cmakelists_dir = os.path.join(ANDROID_BUILD_TOP,
+ 'out/development/ide/clion/art')
+
+ # Prepare a list of directories containing generated CMakeLists files for sub projects.
+ cmake_sub_dirs = set()
+ for root, dirs, files in os.walk(out_art_cmakelists_dir):
+ for name in files:
+ if name == 'CMakeLists.txt':
+ if (os.path.samefile(root, out_art_cmakelists_dir)):
+ continue
+ if arch not in root:
+ continue
+ cmake_sub_dir = cmake_sub_dirs.add(root.replace(out_art_cmakelists_dir,
+ '.'))
+
+ # Generate CMakeLists file.
+ f = open(os.path.join(out_art_cmakelists_dir, 'CMakeLists.txt'), 'w')
+ f.write('cmake_minimum_required(VERSION 3.6)\n')
+ f.write('project(%s)\n' % (project_name))
+
+ for dr in cmake_sub_dirs:
+ f.write('add_subdirectory(%s)\n' % (dr))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc
index 016d708565..7990c6cf8e 100644
--- a/tools/jfuzz/jfuzz.cc
+++ b/tools/jfuzz/jfuzz.cc
@@ -55,7 +55,7 @@ static constexpr const char* kRelOps[] = { "==", "!=", ">", ">=", "<", "<="
* to preserve the property that a given version of JFuzz yields the same
* fuzzed program for a deterministic random seed.
*/
-const char* VERSION = "1.3";
+const char* VERSION = "1.4";
/*
* Maximum number of array dimensions, together with corresponding maximum size
@@ -804,13 +804,6 @@ class JFuzz {
return emitAssignment(); // fall back
}
- // TODO: remove this
- // The jack bug b/28862040 prevents generating while/do-while loops because otherwise
- // we get dozens of reports on the same issue per nightly/ run.
- if (true) {
- return emitAssignment();
- }
-
bool isWhile = random1(2) == 1;
fputs("{\n", out_);
indentation_ += 2;
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index 7e72aa1d92..58bc7374d3 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -524,7 +524,9 @@ class JFuzzTester(object):
jfuzz_args = ['\'-{0}\''.format(arg)
for arg in jfuzz_cmd_str.strip().split(' -')][1:]
wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args]
- repro_cmd_str = (os.path.basename(__file__) + ' --num_tests 1 ' +
+ repro_cmd_str = (os.path.basename(__file__) +
+ ' --num_tests=1 ' +
+ ('--use_dx ' if self._use_dx else '') +
' '.join(wrapped_args))
comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format(
jfuzz_ver, jfuzz_cmd_str, repro_cmd_str)