diff options
author | Brian Carlstrom <bdc@google.com> | 2013-07-12 13:46:57 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2013-07-12 17:49:01 -0700 |
commit | 7940e44f4517de5e2634a7e07d58d0fb26160513 (patch) | |
tree | ac90242d96229a6942f6e24ab137bc1f8f2e0025 /runtime/native | |
parent | 5cd9e3b122f276f610980cbaf0d2ad6ed4cd9088 (diff) | |
download | android_art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.gz android_art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.bz2 android_art-7940e44f4517de5e2634a7e07d58d0fb26160513.zip |
Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees
to prevent dependency creep. They can now be individually built
without rebuilding the rest of the art projects. dalvikvm and jdwpspy
were already this way. Builds in the art directory should behave as
before, building everything including tests.
Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
Diffstat (limited to 'runtime/native')
22 files changed, 3585 insertions, 0 deletions
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc new file mode 100644 index 0000000000..b9838f879a --- /dev/null +++ b/runtime/native/dalvik_system_DexFile.cc @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2008 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 <unistd.h> + +#include "base/logging.h" +#include "class_linker.h" +#include "common_throws.h" +#include "dex_file-inl.h" +#include "gc/space/image_space.h" +#include "gc/space/space-inl.h" +#include "image.h" +#include "jni_internal.h" +#include "mirror/class_loader.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "oat.h" +#include "os.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "ScopedLocalRef.h" +#include "ScopedUtfChars.h" +#include "toStringArray.h" +#include "zip_archive.h" + +namespace art { + +// A smart pointer that provides read-only access to a Java string's UTF chars. +// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if +// passed a null jstring. The correct idiom is: +// +// NullableScopedUtfChars name(env, javaName); +// if (env->ExceptionCheck()) { +// return NULL; +// } +// // ... use name.c_str() +// +// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option. +class NullableScopedUtfChars { + public: + NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) { + mUtfChars = (s != NULL) ? env->GetStringUTFChars(s, NULL) : NULL; + } + + ~NullableScopedUtfChars() { + if (mUtfChars) { + mEnv->ReleaseStringUTFChars(mString, mUtfChars); + } + } + + const char* c_str() const { + return mUtfChars; + } + + size_t size() const { + return strlen(mUtfChars); + } + + // Element access. + const char& operator[](size_t n) const { + return mUtfChars[n]; + } + + private: + JNIEnv* mEnv; + jstring mString; + const char* mUtfChars; + + // Disallow copy and assignment. + NullableScopedUtfChars(const NullableScopedUtfChars&); + void operator=(const NullableScopedUtfChars&); +}; + +static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) { + ScopedUtfChars sourceName(env, javaSourceName); + if (sourceName.c_str() == NULL) { + return 0; + } + std::string source(sourceName.c_str()); + NullableScopedUtfChars outputName(env, javaOutputName); + if (env->ExceptionCheck()) { + return 0; + } + ScopedObjectAccess soa(env); + const DexFile* dex_file; + if (outputName.c_str() == NULL) { + dex_file = Runtime::Current()->GetClassLinker()->FindDexFileInOatFileFromDexLocation(source); + } else { + std::string output(outputName.c_str()); + dex_file = + Runtime::Current()->GetClassLinker()->FindOrCreateOatFileForDexLocation(source, output); + } + if (dex_file == NULL) { + LOG(WARNING) << "Failed to open dex file: " << source; + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;", + "Unable to open dex file: %s", source.c_str()); + return 0; + } + return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file)); +} + +static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address)); + if (dex_file == NULL) { + ThrowNullPointerException(NULL, "dex_file == null"); + } + return dex_file; +} + +static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) { + const DexFile* dex_file; + { + ScopedObjectAccess soa(env); + dex_file = toDexFile(cookie); + } + if (dex_file == NULL) { + return; + } + if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { + return; + } + delete dex_file; +} + +static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, + jint cookie) { + ScopedObjectAccess soa(env); + const DexFile* dex_file = toDexFile(cookie); + if (dex_file == NULL) { + return NULL; + } + ScopedUtfChars class_name(env, javaName); + if (class_name.c_str() == NULL) { + return NULL; + } + const std::string descriptor(DotToDescriptor(class_name.c_str())); + const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor); + if (dex_class_def == NULL) { + return NULL; + } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + class_linker->RegisterDexFile(*dex_file); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader); + mirror::Class* result = class_linker->DefineClass(descriptor, class_loader, *dex_file, *dex_class_def); + return soa.AddLocalReference<jclass>(result); +} + +static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) { + const DexFile* dex_file; + { + ScopedObjectAccess soa(env); + dex_file = toDexFile(cookie); + } + if (dex_file == NULL) { + return NULL; + } + + std::vector<std::string> class_names; + for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + const char* descriptor = dex_file->GetClassDescriptor(class_def); + class_names.push_back(DescriptorToDot(descriptor)); + } + return toStringArray(env, class_names); +} + +static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { + bool debug_logging = false; + + ScopedUtfChars filename(env, javaFilename); + if (filename.c_str() == NULL) { + LOG(ERROR) << "DexFile_isDexOptNeeded null filename"; + return JNI_TRUE; + } + + if (!OS::FileExists(filename.c_str())) { + LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist"; + ScopedObjectAccess soa(env); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;", + "%s", filename.c_str()); + return JNI_TRUE; + } + + // Always treat elements of the bootclasspath as up-to-date. The + // fact that code is running at all means that this should be true. + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath(); + for (size_t i = 0; i < boot_class_path.size(); i++) { + if (boot_class_path[i]->GetLocation() == filename.c_str()) { + if (debug_logging) { + LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str(); + } + return JNI_FALSE; + } + } + + // Check if we have an odex file next to the dex file. + std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str())); + UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL)); + if (oat_file.get() != NULL) { + ScopedObjectAccess soa(env); + const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str()); + if (oat_dex_file == NULL) { + if (debug_logging) { + LOG(INFO) << "DexFile_isDexOptNeeded GetOatDexFile failed"; + } + } else { + uint32_t location_checksum; + // If we have no classes.dex checksum such as in a user build, assume up-to-date. + if (!DexFile::GetChecksum(filename.c_str(), location_checksum)) { + if (debug_logging) { + LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: " + << filename.c_str(); + } + return JNI_FALSE; + } + if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { + if (debug_logging) { + LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename + << " is up-to-date checksum compared to " << filename.c_str(); + } + return JNI_FALSE; + } + } + } + + // Check if we have an oat file in the cache + std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str())); + oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL)); + if (oat_file.get() == NULL) { + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " does not exist for " << filename.c_str(); + return JNI_TRUE; + } + + gc::Heap* heap = runtime->GetHeap(); + const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); + // TODO: C++0x auto + typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; + for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { + if ((*it)->IsImageSpace()) { + // TODO: Ensure this works with multiple image spaces. + const ImageHeader& image_header = (*it)->AsImageSpace()->GetImageHeader(); + if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) { + ScopedObjectAccess soa(env); + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " has out-of-date oat checksum compared to " + << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); + return JNI_TRUE; + } + if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() + != reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) { + ScopedObjectAccess soa(env); + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " has out-of-date oat begin compared to " + << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); + return JNI_TRUE; + } + } + } + + ScopedObjectAccess soa(env); + uint32_t location_checksum; + if (!DexFile::GetChecksum(filename.c_str(), location_checksum)) { + LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str(); + return JNI_TRUE; + } + + if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " has out-of-date checksum compared to " << filename.c_str(); + return JNI_TRUE; + } + + if (debug_logging) { + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " is up-to-date for " << filename.c_str(); + } + return JNI_FALSE; +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(DexFile, closeDexFile, "(I)V"), + NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;"), + NATIVE_METHOD(DexFile, getClassNameList, "(I)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), + NATIVE_METHOD(DexFile, openDexFile, "(Ljava/lang/String;Ljava/lang/String;I)I"), +}; + +void register_dalvik_system_DexFile(JNIEnv* env) { + REGISTER_NATIVE_METHODS("dalvik/system/DexFile"); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc new file mode 100644 index 0000000000..992998eb66 --- /dev/null +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2008 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 <string.h> +#include <unistd.h> + +#include "class_linker.h" +#include "common_throws.h" +#include "debugger.h" +#include "hprof/hprof.h" +#include "jni_internal.h" +#include "mirror/class.h" +#include "ScopedUtfChars.h" +#include "scoped_thread_state_change.h" +#include "toStringArray.h" +#include "trace.h" + +namespace art { + +static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) { + std::vector<std::string> features; + features.push_back("method-trace-profiling"); + features.push_back("method-trace-profiling-streaming"); + features.push_back("hprof-heap-dump"); + features.push_back("hprof-heap-dump-streaming"); + return toStringArray(env, features); +} + +static void VMDebug_startAllocCounting(JNIEnv*, jclass) { + Runtime::Current()->SetStatsEnabled(true); +} + +static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { + Runtime::Current()->SetStatsEnabled(false); +} + +static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { + return Runtime::Current()->GetStat(kind); +} + +static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { + Runtime::Current()->ResetStats(kinds); +} + +static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags) { + Trace::Start("[DDMS]", -1, bufferSize, flags, true); +} + +static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, + jobject javaFd, jint bufferSize, jint flags) { + int originalFd = jniGetFDFromFileDescriptor(env, javaFd); + if (originalFd < 0) { + return; + } + + int fd = dup(originalFd); + if (fd < 0) { + ScopedObjectAccess soa(env); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/RuntimeException;", + "dup(%d) failed: %s", originalFd, strerror(errno)); + return; + } + + ScopedUtfChars traceFilename(env, javaTraceFilename); + if (traceFilename.c_str() == NULL) { + return; + } + Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false); +} + +static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, + jint bufferSize, jint flags) { + ScopedUtfChars traceFilename(env, javaTraceFilename); + if (traceFilename.c_str() == NULL) { + return; + } + Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false); +} + +static jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) { + return Trace::IsMethodTracingActive(); +} + +static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { + Trace::Stop(); +} + +static void VMDebug_startEmulatorTracing(JNIEnv*, jclass) { + UNIMPLEMENTED(WARNING); + //dvmEmulatorTraceStart(); +} + +static void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) { + UNIMPLEMENTED(WARNING); + //dvmEmulatorTraceStop(); +} + +static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { + return Dbg::IsDebuggerActive(); +} + +static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { + return Dbg::IsJdwpConfigured(); +} + +static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { + return Dbg::LastDebuggerActivity(); +} + +static void ThrowUnsupportedOperationException(JNIEnv* env) { + ScopedObjectAccess soa(env); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewException(throw_location, "Ljava/lang/UnsupportedOperationException;", NULL); +} + +static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { + ThrowUnsupportedOperationException(env); +} + +static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) { + ThrowUnsupportedOperationException(env); +} + +static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) { + ThrowUnsupportedOperationException(env); +} + +static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) { + ThrowUnsupportedOperationException(env); +} + +static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { + ScopedObjectAccess soa(env); + return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags); +} + +static jint VMDebug_getLoadedClassCount(JNIEnv*, jclass) { + return Runtime::Current()->GetClassLinker()->NumLoadedClasses(); +} + +/* + * Returns the thread-specific CPU-time clock value for the current thread, + * or -1 if the feature isn't supported. + */ +static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) { + return ThreadCpuNanoTime(); +} + +/* + * static void dumpHprofData(String fileName, FileDescriptor fd) + * + * Cause "hprof" data to be dumped. We can throw an IOException if an + * error occurs during file handling. + */ +static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) { + // Only one of these may be NULL. + if (javaFilename == NULL && javaFd == NULL) { + ScopedObjectAccess soa(env); + ThrowNullPointerException(NULL, "fileName == null && fd == null"); + return; + } + + std::string filename; + if (javaFilename != NULL) { + ScopedUtfChars chars(env, javaFilename); + if (env->ExceptionCheck()) { + return; + } + filename = chars.c_str(); + } else { + filename = "[fd]"; + } + + int fd = -1; + if (javaFd != NULL) { + fd = jniGetFDFromFileDescriptor(env, javaFd); + if (fd < 0) { + ScopedObjectAccess soa(env); + ThrowRuntimeException("Invalid file descriptor"); + return; + } + } + + hprof::DumpHeap(filename.c_str(), fd, false); +} + +static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) { + hprof::DumpHeap("[DDMS]", -1, true); +} + +static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + LOG(INFO) << "--- reference table dump ---"; + + soa.Env()->DumpReferenceTables(LOG(INFO)); + soa.Vm()->DumpReferenceTables(LOG(INFO)); + + LOG(INFO) << "---"; +} + +static void VMDebug_crash(JNIEnv*, jclass) { + LOG(FATAL) << "Crashing runtime on request"; +} + +static void VMDebug_infopoint(JNIEnv*, jclass, jint id) { + LOG(INFO) << "VMDebug infopoint " << id << " hit"; +} + +static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, + jboolean countAssignable) { + ScopedObjectAccess soa(env); + mirror::Class* c = soa.Decode<mirror::Class*>(javaClass); + if (c == NULL) { + return 0; + } + std::vector<mirror::Class*> classes; + classes.push_back(c); + uint64_t count = 0; + Runtime::Current()->GetHeap()->CountInstances(classes, countAssignable, &count); + return count; +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), + NATIVE_METHOD(VMDebug, crash, "()V"), + NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"), + NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), + NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), + NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), + NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), + NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), + NATIVE_METHOD(VMDebug, infopoint, "(I)V"), + NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"), + NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"), + NATIVE_METHOD(VMDebug, isMethodTracingActive, "()Z"), + NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"), + NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"), + NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), + NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"), + NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), + NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"), + NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"), + NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(II)V"), + NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V"), + NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;II)V"), + NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), + NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), + NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), + NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), + NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"), +}; + +void register_dalvik_system_VMDebug(JNIEnv* env) { + REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc new file mode 100644 index 0000000000..ce3cc932a0 --- /dev/null +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2008 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 <limits.h> + +#include "class_linker.h" +#include "common_throws.h" +#include "debugger.h" +#include "dex_file-inl.h" +#include "gc/allocator/dlmalloc.h" +#include "gc/space/dlmalloc_space.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "mirror/object.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "scoped_thread_state_change.h" +#include "thread.h" +#include "thread_list.h" +#include "toStringArray.h" + +namespace art { + +static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) { + return Runtime::Current()->GetHeap()->GetTargetHeapUtilization(); +} + +static void VMRuntime_nativeSetTargetHeapUtilization(JNIEnv*, jobject, jfloat target) { + Runtime::Current()->GetHeap()->SetTargetHeapUtilization(target); +} + +static void VMRuntime_startJitCompilation(JNIEnv*, jobject) { +} + +static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { +} + +static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { + ScopedObjectAccess soa(env); +#ifdef MOVING_GARBAGE_COLLECTOR + // TODO: right now, we don't have a copying collector, so there's no need + // to do anything special here, but we ought to pass the non-movability + // through to the allocator. + UNIMPLEMENTED(FATAL); +#endif + + mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); + if (element_class == NULL) { + ThrowNullPointerException(NULL, "element class == null"); + return NULL; + } + if (length < 0) { + ThrowNegativeArraySizeException(length); + return NULL; + } + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + std::string descriptor; + descriptor += "["; + descriptor += ClassHelper(element_class).GetDescriptor(); + mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), NULL); + mirror::Array* result = mirror::Array::Alloc(soa.Self(), array_class, length); + if (result == NULL) { + return NULL; + } + return soa.AddLocalReference<jobject>(result); +} + +static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { + if (javaArray == NULL) { // Most likely allocation failed + return 0; + } + ScopedObjectAccess soa(env); + mirror::Array* array = soa.Decode<mirror::Array*>(javaArray); + if (!array->IsArrayInstance()) { + ThrowIllegalArgumentException(NULL, "not an array"); + return 0; + } + // TODO: we should also check that this is a non-movable array. + return reinterpret_cast<uintptr_t>(array->GetRawData(array->GetClass()->GetComponentSize())); +} + +static void VMRuntime_clearGrowthLimit(JNIEnv*, jobject) { + Runtime::Current()->GetHeap()->ClearGrowthLimit(); +} + +static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) { + return Dbg::IsDebuggerActive(); +} + +static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) { + return toStringArray(env, Runtime::Current()->GetProperties()); +} + +// This is for backward compatibility with dalvik which returned the +// meaningless "." when no boot classpath or classpath was +// specified. Unfortunately, some tests were using java.class.path to +// lookup relative file locations, so they are counting on this to be +// ".", presumably some applications or libraries could have as well. +static const char* DefaultToDot(const std::string& class_path) { + return class_path.empty() ? "." : class_path.c_str(); +} + +static jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) { + return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetBootClassPathString())); +} + +static jstring VMRuntime_classPath(JNIEnv* env, jobject) { + return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetClassPathString())); +} + +static jstring VMRuntime_vmVersion(JNIEnv* env, jobject) { + return env->NewStringUTF(Runtime::Current()->GetVersion()); +} + +static jstring VMRuntime_vmLibrary(JNIEnv* env, jobject) { + return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so"); +} + +#if !defined(ART_USE_PORTABLE_COMPILER) +static void DisableCheckJniCallback(Thread* t, void*) { + t->GetJniEnv()->SetCheckJniEnabled(false); +} +#endif + +static void VMRuntime_setTargetSdkVersion(JNIEnv* env, jobject, jint targetSdkVersion) { + // This is the target SDK version of the app we're about to run. + // Note that targetSdkVersion may be CUR_DEVELOPMENT (10000). + // Note that targetSdkVersion may be 0, meaning "current". + if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) { + Runtime* runtime = Runtime::Current(); + JavaVMExt* vm = runtime->GetJavaVM(); + +#if !defined(ART_USE_PORTABLE_COMPILER) + if (vm->check_jni) { + LOG(WARNING) << "Turning off CheckJNI so we can turn on JNI app bug workarounds..."; + Thread* self = static_cast<JNIEnvExt*>(env)->self; + MutexLock mu(self, *Locks::thread_list_lock_); + vm->SetCheckJniEnabled(false); + runtime->GetThreadList()->ForEach(DisableCheckJniCallback, NULL); + } + + LOG(INFO) << "Turning on JNI app bug workarounds for target SDK version " + << targetSdkVersion << "..."; + + vm->work_around_app_jni_bugs = true; +#else + UNUSED(env); + LOG(WARNING) << "LLVM does not work-around app jni bugs."; + vm->work_around_app_jni_bugs = false; +#endif + } +} + +static void VMRuntime_trimHeap(JNIEnv*, jobject) { + uint64_t start_ns = NanoTime(); + + // Trim the managed heap. + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::DlMallocSpace* alloc_space = heap->GetAllocSpace(); + size_t alloc_space_size = alloc_space->Size(); + float managed_utilization = + static_cast<float>(alloc_space->GetBytesAllocated()) / alloc_space_size; + size_t managed_reclaimed = heap->Trim(); + + uint64_t gc_heap_end_ns = NanoTime(); + + // Trim the native heap. + dlmalloc_trim(0); + size_t native_reclaimed = 0; + dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed); + + uint64_t end_ns = NanoTime(); + + LOG(INFO) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns) + << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration=" + << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed) + << ") heaps. Managed heap utilization of " << static_cast<int>(100 * managed_utilization) + << "%."; +} + +static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { + Thread* self = static_cast<JNIEnvExt*>(env)->self; + Runtime::Current()->GetHeap()->ConcurrentGC(self); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), + NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"), + NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), + NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), + NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), + NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), + NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), + NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), + NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, setTargetSdkVersion, "(I)V"), + NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"), + NATIVE_METHOD(VMRuntime, trimHeap, "()V"), + NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), +}; + +void register_dalvik_system_VMRuntime(JNIEnv* env) { + REGISTER_NATIVE_METHODS("dalvik/system/VMRuntime"); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc new file mode 100644 index 0000000000..1a80d6286b --- /dev/null +++ b/runtime/native/dalvik_system_VMStack.cc @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2008 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 "jni_internal.h" +#include "nth_caller_visitor.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" +#include "thread_list.h" + +namespace art { + +static jobject GetThreadStack(JNIEnv* env, jobject peer) { + { + ScopedObjectAccess soa(env); + if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) { + return soa.Self()->CreateInternalStackTrace(soa); + } + } + // Suspend thread to build stack trace. + bool timed_out; + Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out); + if (thread != NULL) { + jobject trace; + { + ScopedObjectAccess soa(env); + trace = thread->CreateInternalStackTrace(soa); + } + // Restart suspended thread. + Runtime::Current()->GetThreadList()->Resume(thread, true); + return trace; + } else { + if (timed_out) { + LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a " + "generous timeout."; + } + return NULL; + } +} + +static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread, + jobjectArray javaSteArray) { + jobject trace = GetThreadStack(env, javaThread); + if (trace == NULL) { + return 0; + } + int32_t depth; + Thread::InternalStackTraceToStackTraceElementArray(env, trace, javaSteArray, &depth); + return depth; +} + +// Returns the defining class loader of the caller's caller. +static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + NthCallerVisitor visitor(soa.Self(), 2); + visitor.WalkStack(); + return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader()); +} + +static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject javaBootstrap, + jobject javaSystem) { + struct ClosestUserClassLoaderVisitor : public StackVisitor { + ClosestUserClassLoaderVisitor(Thread* thread, mirror::Object* bootstrap, mirror::Object* system) + : StackVisitor(thread, NULL), bootstrap(bootstrap), system(system), class_loader(NULL) {} + + bool VisitFrame() { + DCHECK(class_loader == NULL); + mirror::Class* c = GetMethod()->GetDeclaringClass(); + mirror::Object* cl = c->GetClassLoader(); + if (cl != NULL && cl != bootstrap && cl != system) { + class_loader = cl; + return false; + } + return true; + } + + mirror::Object* bootstrap; + mirror::Object* system; + mirror::Object* class_loader; + }; + ScopedObjectAccess soa(env); + mirror::Object* bootstrap = soa.Decode<mirror::Object*>(javaBootstrap); + mirror::Object* system = soa.Decode<mirror::Object*>(javaSystem); + ClosestUserClassLoaderVisitor visitor(soa.Self(), bootstrap, system); + visitor.WalkStack(); + return soa.AddLocalReference<jobject>(visitor.class_loader); +} + +// Returns the class of the caller's caller's caller. +static jclass VMStack_getStackClass2(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + NthCallerVisitor visitor(soa.Self(), 3); + visitor.WalkStack(); + return soa.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass()); +} + +static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject javaThread) { + jobject trace = GetThreadStack(env, javaThread); + if (trace == NULL) { + return NULL; + } + return Thread::InternalStackTraceToStackTraceElementArray(env, trace); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"), + NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"), + NATIVE_METHOD(VMStack, getClosestUserClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"), + NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"), + NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"), +}; + +void register_dalvik_system_VMStack(JNIEnv* env) { + REGISTER_NATIVE_METHODS("dalvik/system/VMStack"); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc new file mode 100644 index 0000000000..9b995f421d --- /dev/null +++ b/runtime/native/dalvik_system_Zygote.cc @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2008 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. + */ + +// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc +#include <sys/mount.h> +#include <linux/fs.h> + +#include <grp.h> +#include <paths.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "cutils/fs.h" +#include "cutils/multiuser.h" +#include "cutils/sched_policy.h" +#include "debugger.h" +#include "jni_internal.h" +#include "JNIHelp.h" +#include "ScopedLocalRef.h" +#include "ScopedPrimitiveArray.h" +#include "ScopedUtfChars.h" +#include "thread.h" + +#if defined(HAVE_PRCTL) +#include <sys/prctl.h> +#endif + +#include <selinux/android.h> + +#if defined(__linux__) +#include <sys/personality.h> +#include <sys/utsname.h> +#include <sys/capability.h> +#endif + +namespace art { + +static pid_t gSystemServerPid = 0; + +// Must match values in dalvik.system.Zygote. +enum MountExternalKind { + MOUNT_EXTERNAL_NONE = 0, + MOUNT_EXTERNAL_SINGLEUSER = 1, + MOUNT_EXTERNAL_MULTIUSER = 2, + MOUNT_EXTERNAL_MULTIUSER_ALL = 3, +}; + +// This signal handler is for zygote mode, since the zygote must reap its children +static void SigChldHandler(int /*signal_number*/) { + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + // Log process-death status that we care about. In general it is + // not safe to call LOG(...) from a signal handler because of + // possible reentrancy. However, we know a priori that the + // current implementation of LOG() is safe to call from a SIGCHLD + // handler in the zygote process. If the LOG() implementation + // changes its locking strategy or its use of syscalls within the + // lazy-init critical section, its use here may become unsafe. + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + LOG(INFO) << "Process " << pid << " exited cleanly (" << WEXITSTATUS(status) << ")"; + } else if (false) { + LOG(INFO) << "Process " << pid << " exited cleanly (" << WEXITSTATUS(status) << ")"; + } + } else if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGKILL) { + LOG(INFO) << "Process " << pid << " terminated by signal (" << WTERMSIG(status) << ")"; + } else if (false) { + LOG(INFO) << "Process " << pid << " terminated by signal (" << WTERMSIG(status) << ")"; + } +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + LOG(INFO) << "Process " << pid << " dumped core"; + } +#endif /* ifdef WCOREDUMP */ + } + + // If the just-crashed process is the system_server, bring down zygote + // so that it is restarted by init and system server will be restarted + // from there. + if (pid == gSystemServerPid) { + LOG(ERROR) << "Exit zygote because system server (" << pid << ") has terminated"; + kill(getpid(), SIGKILL); + } + } + + if (pid < 0) { + PLOG(WARNING) << "Zygote SIGCHLD error in waitpid"; + } +} + +// Configures the SIGCHLD handler for the zygote process. This is configured +// very late, because earlier in the runtime we may fork() and exec() +// other processes, and we want to waitpid() for those rather than +// have them be harvested immediately. +// +// This ends up being called repeatedly before each fork(), but there's +// no real harm in that. +static void SetSigChldHandler() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SigChldHandler; + + int err = sigaction(SIGCHLD, &sa, NULL); + if (err < 0) { + PLOG(WARNING) << "Error setting SIGCHLD handler"; + } +} + +// Sets the SIGCHLD handler back to default behavior in zygote children. +static void UnsetSigChldHandler() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + + int err = sigaction(SIGCHLD, &sa, NULL); + if (err < 0) { + PLOG(WARNING) << "Error unsetting SIGCHLD handler"; + } +} + +// Calls POSIX setgroups() using the int[] object as an argument. +// A NULL argument is tolerated. +static void SetGids(JNIEnv* env, jintArray javaGids) { + if (javaGids == NULL) { + return; + } + + COMPILE_ASSERT(sizeof(gid_t) == sizeof(jint), sizeof_gid_and_jint_are_differerent); + ScopedIntArrayRO gids(env, javaGids); + CHECK(gids.get() != NULL); + int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0])); + if (rc == -1) { + PLOG(FATAL) << "setgroups failed"; + } +} + +// Sets the resource limits via setrlimit(2) for the values in the +// two-dimensional array of integers that's passed in. The second dimension +// contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is +// treated as an empty array. +static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) { + if (javaRlimits == NULL) { + return; + } + + rlimit rlim; + memset(&rlim, 0, sizeof(rlim)); + + for (int i = 0; i < env->GetArrayLength(javaRlimits); ++i) { + ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i)); + ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get())); + if (javaRlimit.size() != 3) { + LOG(FATAL) << "rlimits array must have a second dimension of size 3"; + } + + rlim.rlim_cur = javaRlimit[1]; + rlim.rlim_max = javaRlimit[2]; + + int rc = setrlimit(javaRlimit[0], &rlim); + if (rc == -1) { + PLOG(FATAL) << "setrlimit(" << javaRlimit[0] << ", " + << "{" << rlim.rlim_cur << ", " << rlim.rlim_max << "}) failed"; + } + } +} + +#if defined(HAVE_ANDROID_OS) + +// The debug malloc library needs to know whether it's the zygote or a child. +extern "C" int gMallocLeakZygoteChild; + +static void EnableDebugger() { + // To let a non-privileged gdbserver attach to this + // process, we must set our dumpable flag. + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { + PLOG(ERROR) << "prctl(PR_SET_DUMPABLE) failed for pid " << getpid(); + } + // We don't want core dumps, though, so set the core dump size to 0. + rlimit rl; + rl.rlim_cur = 0; + rl.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rl) == -1) { + PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid(); + } +} + +static void EnableKeepCapabilities() { + int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (rc == -1) { + PLOG(FATAL) << "prctl(PR_SET_KEEPCAPS) failed"; + } +} + +static void DropCapabilitiesBoundingSet() { + for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) { + if (i == CAP_NET_RAW) { + // Don't break /system/bin/ping + continue; + } + int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); + if (rc == -1) { + if (errno == EINVAL) { + PLOG(ERROR) << "prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify " + << "your kernel is compiled with file capabilities support"; + } else { + PLOG(FATAL) << "prctl(PR_CAPBSET_DROP) failed"; + } + } + } +} + +static void SetCapabilities(int64_t permitted, int64_t effective) { + __user_cap_header_struct capheader; + __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + capdata.effective = effective; + capdata.permitted = permitted; + + if (capset(&capheader, &capdata) != 0) { + PLOG(FATAL) << "capset(" << permitted << ", " << effective << ") failed"; + } +} + +static void SetSchedulerPolicy() { + errno = -set_sched_policy(0, SP_DEFAULT); + if (errno != 0) { + PLOG(FATAL) << "set_sched_policy(0, SP_DEFAULT) failed"; + } +} + +#else + +static int gMallocLeakZygoteChild = 0; + +static void EnableDebugger() {} +static void EnableKeepCapabilities() {} +static void DropCapabilitiesBoundingSet() {}; +static void SetCapabilities(int64_t, int64_t) {} +static void SetSchedulerPolicy() {} + +#endif + +static void EnableDebugFeatures(uint32_t debug_flags) { + // Must match values in dalvik.system.Zygote. + enum { + DEBUG_ENABLE_DEBUGGER = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, + DEBUG_ENABLE_SAFEMODE = 1 << 3, + DEBUG_ENABLE_JNI_LOGGING = 1 << 4, + }; + + if ((debug_flags & DEBUG_ENABLE_CHECKJNI) != 0) { + Runtime* runtime = Runtime::Current(); + JavaVMExt* vm = runtime->GetJavaVM(); + if (!vm->check_jni) { + LOG(DEBUG) << "Late-enabling -Xcheck:jni"; + vm->SetCheckJniEnabled(true); + // There's only one thread running at this point, so only one JNIEnv to fix up. + Thread::Current()->GetJniEnv()->SetCheckJniEnabled(true); + } else { + LOG(DEBUG) << "Not late-enabling -Xcheck:jni (already on)"; + } + debug_flags &= ~DEBUG_ENABLE_CHECKJNI; + } + + if ((debug_flags & DEBUG_ENABLE_JNI_LOGGING) != 0) { + gLogVerbosity.third_party_jni = true; + debug_flags &= ~DEBUG_ENABLE_JNI_LOGGING; + } + + Dbg::SetJdwpAllowed((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0); + if ((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0) { + EnableDebugger(); + } + debug_flags &= ~DEBUG_ENABLE_DEBUGGER; + + // These two are for backwards compatibility with Dalvik. + debug_flags &= ~DEBUG_ENABLE_ASSERT; + debug_flags &= ~DEBUG_ENABLE_SAFEMODE; + + if (debug_flags != 0) { + LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags); + } +} + +// Create a private mount namespace and bind mount appropriate emulated +// storage for the given user. +static bool MountEmulatedStorage(uid_t uid, jint mount_mode) { + if (mount_mode == MOUNT_EXTERNAL_NONE) { + return true; + } + + // See storage config details at http://source.android.com/tech/storage/ + userid_t user_id = multiuser_get_user_id(uid); + + // Create a second private mount namespace for our process + if (unshare(CLONE_NEWNS) == -1) { + PLOG(WARNING) << "Failed to unshare()"; + return false; + } + + // Create bind mounts to expose external storage + if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) { + // These paths must already be created by init.rc + const char* source = getenv("EMULATED_STORAGE_SOURCE"); + const char* target = getenv("EMULATED_STORAGE_TARGET"); + const char* legacy = getenv("EXTERNAL_STORAGE"); + if (source == NULL || target == NULL || legacy == NULL) { + LOG(WARNING) << "Storage environment undefined; unable to provide external storage"; + return false; + } + + // Prepare source paths + + // /mnt/shell/emulated/0 + std::string source_user(StringPrintf("%s/%d", source, user_id)); + // /mnt/shell/emulated/obb + std::string source_obb(StringPrintf("%s/obb", source)); + // /storage/emulated/0 + std::string target_user(StringPrintf("%s/%d", target, user_id)); + + if (fs_prepare_dir(source_user.c_str(), 0000, 0, 0) == -1 + || fs_prepare_dir(source_obb.c_str(), 0000, 0, 0) == -1 + || fs_prepare_dir(target_user.c_str(), 0000, 0, 0) == -1) { + return false; + } + + if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) { + // Mount entire external storage tree for all users + if (mount(source, target, NULL, MS_BIND, NULL) == -1) { + PLOG(WARNING) << "Failed to mount " << source << " to " << target; + return false; + } + } else { + // Only mount user-specific external storage + if (mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL) == -1) { + PLOG(WARNING) << "Failed to mount " << source_user << " to " << target_user; + return false; + } + } + + // Now that user is mounted, prepare and mount OBB storage + // into place for current user + + // /storage/emulated/0/Android + std::string target_android(StringPrintf("%s/%d/Android", target, user_id)); + // /storage/emulated/0/Android/obb + std::string target_obb(StringPrintf("%s/%d/Android/obb", target, user_id)); + + if (fs_prepare_dir(target_android.c_str(), 0000, 0, 0) == -1 + || fs_prepare_dir(target_obb.c_str(), 0000, 0, 0) == -1 + || fs_prepare_dir(legacy, 0000, 0, 0) == -1) { + return false; + } + if (mount(source_obb.c_str(), target_obb.c_str(), NULL, MS_BIND, NULL) == -1) { + PLOG(WARNING) << "Failed to mount " << source_obb << " to " << target_obb; + return false; + } + + // Finally, mount user-specific path into place for legacy users + if (mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL) == -1) { + PLOG(WARNING) << "Failed to mount " << target_user << " to " << legacy; + return false; + } + } else { + LOG(WARNING) << "Mount mode " << mount_mode << " unsupported"; + return false; + } + + return true; +} + +#if defined(__linux__) +static bool NeedsNoRandomizeWorkaround() { +#if !defined(__arm__) + return false; +#else + int major; + int minor; + struct utsname uts; + if (uname(&uts) == -1) { + return false; + } + + if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) { + return false; + } + + // Kernels before 3.4.* need the workaround. + return (major < 3) || ((major == 3) && (minor < 4)); +#endif +} +#endif + +// Utility routine to fork zygote and specialize the child process. +static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids, + jint debug_flags, jobjectArray javaRlimits, + jlong permittedCapabilities, jlong effectiveCapabilities, + jint mount_external, + jstring java_se_info, jstring java_se_name, bool is_system_server) { + Runtime* runtime = Runtime::Current(); + CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote"; + if (!runtime->PreZygoteFork()) { + LOG(FATAL) << "pre-fork heap failed"; + } + + SetSigChldHandler(); + + // Grab thread before fork potentially makes Thread::pthread_key_self_ unusable. + Thread* self = Thread::Current(); + + // dvmDumpLoaderStats("zygote"); // TODO: ? + pid_t pid = fork(); + + if (pid == 0) { + // The child process. + gMallocLeakZygoteChild = 1; + + // Keep capabilities across UID change, unless we're staying root. + if (uid != 0) { + EnableKeepCapabilities(); + } + + DropCapabilitiesBoundingSet(); + + if (!MountEmulatedStorage(uid, mount_external)) { + PLOG(WARNING) << "Failed to mount emulated storage"; + if (errno == ENOTCONN || errno == EROFS) { + // When device is actively encrypting, we get ENOTCONN here + // since FUSE was mounted before the framework restarted. + // When encrypted device is booting, we get EROFS since + // FUSE hasn't been created yet by init. + // In either case, continue without external storage. + } else { + LOG(FATAL) << "Cannot continue without emulated storage"; + } + } + + SetGids(env, javaGids); + + SetRLimits(env, javaRlimits); + + int rc = setresgid(gid, gid, gid); + if (rc == -1) { + PLOG(FATAL) << "setresgid(" << gid << ") failed"; + } + + rc = setresuid(uid, uid, uid); + if (rc == -1) { + PLOG(FATAL) << "setresuid(" << uid << ") failed"; + } + +#if defined(__linux__) + if (NeedsNoRandomizeWorkaround()) { + // Work around ARM kernel ASLR lossage (http://b/5817320). + int old_personality = personality(0xffffffff); + int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE); + if (new_personality == -1) { + PLOG(WARNING) << "personality(" << new_personality << ") failed"; + } + } +#endif + + SetCapabilities(permittedCapabilities, effectiveCapabilities); + + SetSchedulerPolicy(); + +#if defined(HAVE_ANDROID_OS) + { + const char* se_info_c_str = NULL; + UniquePtr<ScopedUtfChars> se_info; + if (java_se_info != NULL) { + se_info.reset(new ScopedUtfChars(env, java_se_info)); + se_info_c_str = se_info->c_str(); + CHECK(se_info_c_str != NULL); + } + const char* se_name_c_str = NULL; + UniquePtr<ScopedUtfChars> se_name; + if (java_se_name != NULL) { + se_name.reset(new ScopedUtfChars(env, java_se_name)); + se_name_c_str = se_name->c_str(); + CHECK(se_name_c_str != NULL); + } + rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str); + if (rc == -1) { + PLOG(FATAL) << "selinux_android_setcontext(" << uid << ", " + << (is_system_server ? "true" : "false") << ", " + << "\"" << se_info_c_str << "\", \"" << se_name_c_str << "\") failed"; + } + } +#else + UNUSED(is_system_server); + UNUSED(java_se_info); + UNUSED(java_se_name); +#endif + + // Our system thread ID, etc, has changed so reset Thread state. + self->InitAfterFork(); + + EnableDebugFeatures(debug_flags); + + UnsetSigChldHandler(); + runtime->DidForkFromZygote(); + } else if (pid > 0) { + // the parent process + } + return pid; +} + +static jint Zygote_nativeForkAndSpecialize(JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, + jint debug_flags, jobjectArray rlimits, jint mount_external, + jstring se_info, jstring se_name) { + return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external, se_info, se_name, false); +} + +static jint Zygote_nativeForkSystemServer(JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids, + jint debug_flags, jobjectArray rlimits, + jlong permittedCapabilities, jlong effectiveCapabilities) { + pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids, + debug_flags, rlimits, + permittedCapabilities, effectiveCapabilities, + MOUNT_EXTERNAL_NONE, NULL, NULL, true); + if (pid > 0) { + // The zygote process checks whether the child process has died or not. + LOG(INFO) << "System server process " << pid << " has been created"; + gSystemServerPid = pid; + // There is a slight window that the system server process has crashed + // but it went unnoticed because we haven't published its pid yet. So + // we recheck here just to make sure that all is well. + int status; + if (waitpid(pid, &status, WNOHANG) == pid) { + LOG(FATAL) << "System server process " << pid << " has died. Restarting Zygote!"; + } + } + return pid; +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Zygote, nativeForkAndSpecialize, "(II[II[[IILjava/lang/String;Ljava/lang/String;)I"), + NATIVE_METHOD(Zygote, nativeForkSystemServer, "(II[II[[IJJ)I"), +}; + +void register_dalvik_system_Zygote(JNIEnv* env) { + REGISTER_NATIVE_METHODS("dalvik/system/Zygote"); +} + +} // namespace art diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc new file mode 100644 index 0000000000..a7296996da --- /dev/null +++ b/runtime/native/java_lang_Class.cc @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "dex_file-inl.h" +#include "jni_internal.h" +#include "nth_caller_visitor.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/object-inl.h" +#include "mirror/proxy.h" +#include "object_utils.h" +#include "scoped_thread_state_change.h" +#include "ScopedLocalRef.h" +#include "ScopedUtfChars.h" +#include "well_known_classes.h" + +namespace art { + +static mirror::Class* DecodeClass(const ScopedObjectAccess& soa, jobject java_class) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* c = soa.Decode<mirror::Class*>(java_class); + DCHECK(c != NULL); + DCHECK(c->IsClass()); + // TODO: we could EnsureInitialized here, rather than on every reflective get/set or invoke . + // For now, we conservatively preserve the old dalvik behavior. A quick "IsInitialized" check + // every time probably doesn't make much difference to reflection performance anyway. + return c; +} + +// "name" is in "binary name" format, e.g. "dalvik.system.Debug$1". +static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) { + ScopedObjectAccess soa(env); + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + + // We need to validate and convert the name (from x.y.z to x/y/z). This + // is especially handy for array types, since we want to avoid + // auto-generating bogus array classes. + if (!IsValidBinaryClassName(name.c_str())) { + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ClassNotFoundException;", + "Invalid name: %s", name.c_str()); + return NULL; + } + + std::string descriptor(DotToDescriptor(name.c_str())); + mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* c = class_linker->FindClass(descriptor.c_str(), class_loader); + if (c == NULL) { + ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred()); + env->ExceptionClear(); + jthrowable cnfe = reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException, + WellKnownClasses::java_lang_ClassNotFoundException_init, + javaName, cause.get())); + env->Throw(cnfe); + return NULL; + } + if (initialize) { + class_linker->EnsureInitialized(c, true, true); + } + return soa.AddLocalReference<jclass>(c); +} + +static jint Class_getAnnotationDirectoryOffset(JNIEnv* env, jclass javaClass) { + ScopedObjectAccess soa(env); + mirror::Class* c = DecodeClass(soa, javaClass); + if (c->IsPrimitive() || c->IsArrayClass() || c->IsProxyClass()) { + return 0; // primitive, array and proxy classes don't have class definitions + } + const DexFile::ClassDef* class_def = ClassHelper(c).GetClassDef(); + if (class_def == NULL) { + return 0; // not found + } else { + return class_def->annotations_off_; + } +} + +static jobject Class_getDex(JNIEnv* env, jobject javaClass) { + ScopedObjectAccess soa(env); + mirror::Class* c = DecodeClass(soa, javaClass); + + mirror::DexCache* dex_cache = c->GetDexCache(); + if (dex_cache == NULL) { + return NULL; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + if (dex_file == NULL) { + return NULL; + } + return dex_file->GetDexObject(env); +} + +static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { + ScopedObjectAccess soa(env); + mirror::Class* c = DecodeClass(soa, javaThis); + return soa.AddLocalReference<jstring>(c->ComputeName()); +} + +static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) { + ScopedObjectAccess soa(env); + mirror::SynthesizedProxyClass* c = + down_cast<mirror::SynthesizedProxyClass*>(DecodeClass(soa, javaThis)); + return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self())); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), + NATIVE_METHOD(Class, getAnnotationDirectoryOffset, "()I"), + NATIVE_METHOD(Class, getDex, "()Lcom/android/dex/Dex;"), + NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"), + NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"), +}; + +void register_java_lang_Class(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/Class"); +} + +} // namespace art diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc new file mode 100644 index 0000000000..5db7a330a8 --- /dev/null +++ b/runtime/native/java_lang_Object.cc @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008 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 "jni_internal.h" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" + +// TODO: better support for overloading. +#undef NATIVE_METHOD +#define NATIVE_METHOD(className, functionName, signature, identifier) \ + { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) } + +namespace art { + +static jobject Object_internalClone(JNIEnv* env, jobject java_this) { + ScopedObjectAccess soa(env); + mirror::Object* o = soa.Decode<mirror::Object*>(java_this); + return soa.AddLocalReference<jobject>(o->Clone(soa.Self())); +} + +static void Object_notify(JNIEnv* env, jobject java_this) { + ScopedObjectAccess soa(env); + mirror::Object* o = soa.Decode<mirror::Object*>(java_this); + o->Notify(soa.Self()); +} + +static void Object_notifyAll(JNIEnv* env, jobject java_this) { + ScopedObjectAccess soa(env); + mirror::Object* o = soa.Decode<mirror::Object*>(java_this); + o->NotifyAll(soa.Self()); +} + +static void Object_wait(JNIEnv* env, jobject java_this) { + ScopedObjectAccess soa(env); + mirror::Object* o = soa.Decode<mirror::Object*>(java_this); + o->Wait(soa.Self()); +} + +static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) { + ScopedObjectAccess soa(env); + mirror::Object* o = soa.Decode<mirror::Object*>(java_this); + o->Wait(soa.Self(), ms, ns); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;", internalClone), + NATIVE_METHOD(Object, notify, "()V", notify), + NATIVE_METHOD(Object, notifyAll, "()V", notifyAll), + NATIVE_METHOD(Object, wait, "()V", wait), + NATIVE_METHOD(Object, wait, "(JI)V", waitJI), +}; + +void register_java_lang_Object(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/Object"); +} + +} // namespace art diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc new file mode 100644 index 0000000000..e380c17793 --- /dev/null +++ b/runtime/native/java_lang_Runtime.cc @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 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 <dlfcn.h> +#include <limits.h> +#include <unistd.h> + +#include "gc/heap.h" +#include "jni_internal.h" +#include "mirror/class_loader.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "ScopedUtfChars.h" + +namespace art { + +static void Runtime_gc(JNIEnv*, jclass) { + Runtime::Current()->GetHeap()->CollectGarbage(false); +} + +static void Runtime_nativeExit(JNIEnv*, jclass, jint status) { + Runtime::Current()->CallExitHook(status); + exit(status); +} + +static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) { + ScopedObjectAccess soa(env); + ScopedUtfChars filename(env, javaFilename); + if (filename.c_str() == NULL) { + return NULL; + } + + if (javaLdLibraryPath != NULL) { + ScopedUtfChars ldLibraryPath(env, javaLdLibraryPath); + if (ldLibraryPath.c_str() == NULL) { + return NULL; + } + void* sym = dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH"); + if (sym != NULL) { + typedef void (*Fn)(const char*); + Fn android_update_LD_LIBRARY_PATH = reinterpret_cast<Fn>(sym); + (*android_update_LD_LIBRARY_PATH)(ldLibraryPath.c_str()); + } else { + LOG(ERROR) << "android_update_LD_LIBRARY_PATH not found; .so dependencies will not work!"; + } + } + + mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader); + std::string detail; + JavaVMExt* vm = Runtime::Current()->GetJavaVM(); + bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, detail); + if (success) { + return NULL; + } + + // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF. + env->ExceptionClear(); + return env->NewStringUTF(detail.c_str()); +} + +static jlong Runtime_maxMemory(JNIEnv*, jclass) { + return Runtime::Current()->GetHeap()->GetMaxMemory(); +} + +static jlong Runtime_totalMemory(JNIEnv*, jclass) { + return Runtime::Current()->GetHeap()->GetTotalMemory(); +} + +static jlong Runtime_freeMemory(JNIEnv*, jclass) { + return Runtime::Current()->GetHeap()->GetFreeMemory(); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Runtime, freeMemory, "()J"), + NATIVE_METHOD(Runtime, gc, "()V"), + NATIVE_METHOD(Runtime, maxMemory, "()J"), + NATIVE_METHOD(Runtime, nativeExit, "(I)V"), + NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(Runtime, totalMemory, "()J"), +}; + +void register_java_lang_Runtime(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/Runtime"); +} + +} // namespace art diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc new file mode 100644 index 0000000000..3e9c3f36fa --- /dev/null +++ b/runtime/native/java_lang_String.cc @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 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_throws.h" +#include "jni_internal.h" +#include "mirror/string.h" +#include "scoped_thread_state_change.h" +#include "ScopedLocalRef.h" + +namespace art { + +static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) { + ScopedObjectAccess soa(env); + if (UNLIKELY(javaRhs == NULL)) { + ThrowNullPointerException(NULL, "rhs == null"); + return -1; + } else { + return soa.Decode<mirror::String*>(javaThis)->CompareTo(soa.Decode<mirror::String*>(javaRhs)); + } +} + +static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) { + ScopedObjectAccess soa(env); + // This method does not handle supplementary characters. They're dealt with in managed code. + DCHECK_LE(ch, 0xffff); + + mirror::String* s = soa.Decode<mirror::String*>(java_this); + return s->FastIndexOf(ch, start); +} + +static jstring String_intern(JNIEnv* env, jobject javaThis) { + ScopedObjectAccess soa(env); + mirror::String* s = soa.Decode<mirror::String*>(javaThis); + mirror::String* result = s->Intern(); + return soa.AddLocalReference<jstring>(result); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"), + NATIVE_METHOD(String, fastIndexOf, "(II)I"), + NATIVE_METHOD(String, intern, "()Ljava/lang/String;"), +}; + +void register_java_lang_String(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/String"); +} + +} // namespace art diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc new file mode 100644 index 0000000000..2462f2fd8e --- /dev/null +++ b/runtime/native/java_lang_System.cc @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2008 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_throws.h" +#include "gc/accounting/card_table-inl.h" +#include "jni_internal.h" +#include "mirror/array.h" +#include "mirror/class.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "scoped_thread_state_change.h" + +/* + * We make guarantees about the atomicity of accesses to primitive + * variables. These guarantees also apply to elements of arrays. + * In particular, 8-bit, 16-bit, and 32-bit accesses must be atomic and + * must not cause "word tearing". Accesses to 64-bit array elements must + * either be atomic or treated as two 32-bit operations. References are + * always read and written atomically, regardless of the number of bits + * used to represent them. + * + * We can't rely on standard libc functions like memcpy(3) and memmove(3) + * in our implementation of System.arraycopy, because they may copy + * byte-by-byte (either for the full run or for "unaligned" parts at the + * start or end). We need to use functions that guarantee 16-bit or 32-bit + * atomicity as appropriate. + * + * System.arraycopy() is heavily used, so having an efficient implementation + * is important. The bionic libc provides a platform-optimized memory move + * function that should be used when possible. If it's not available, + * the trivial "reference implementation" versions below can be used until + * a proper version can be written. + * + * For these functions, The caller must guarantee that dst/src are aligned + * appropriately for the element type, and that n is a multiple of the + * element size. + */ + +/* + * Works like memmove(), except: + * - if all arguments are at least 32-bit aligned, we guarantee that we + * will use operations that preserve atomicity of 32-bit values + * - if not, we guarantee atomicity of 16-bit values + * + * If all three arguments are not at least 16-bit aligned, the behavior + * of this function is undefined. (We could remove this restriction by + * testing for unaligned values and punting to memmove(), but that's + * not currently useful.) + * + * TODO: add loop for 64-bit alignment + * TODO: use __builtin_prefetch + * TODO: write ARM/MIPS/x86 optimized versions + */ +void MemmoveWords(void* dst, const void* src, size_t n) { + DCHECK_EQ((((uintptr_t) dst | (uintptr_t) src | n) & 0x01), 0U); + + char* d = reinterpret_cast<char*>(dst); + const char* s = reinterpret_cast<const char*>(src); + size_t copyCount; + + // If the source and destination pointers are the same, this is + // an expensive no-op. Testing for an empty move now allows us + // to skip a check later. + if (n == 0 || d == s) { + return; + } + + // Determine if the source and destination buffers will overlap if + // we copy data forward (i.e. *dst++ = *src++). + // + // It's okay if the destination buffer starts before the source and + // there is some overlap, because the reader is always ahead of the + // writer. + if (LIKELY((d < s) || ((size_t)(d - s) >= n))) { + // Copy forward. We prefer 32-bit loads and stores even for 16-bit + // data, so sort that out. + if (((reinterpret_cast<uintptr_t>(d) | reinterpret_cast<uintptr_t>(s)) & 0x03) != 0) { + // Not 32-bit aligned. Two possibilities: + // (1) Congruent, we can align to 32-bit by copying one 16-bit val + // (2) Non-congruent, we can do one of: + // a. copy whole buffer as a series of 16-bit values + // b. load/store 32 bits, using shifts to ensure alignment + // c. just copy the as 32-bit values and assume the CPU + // will do a reasonable job + // + // We're currently using (a), which is suboptimal. + if (((reinterpret_cast<uintptr_t>(d) ^ reinterpret_cast<uintptr_t>(s)) & 0x03) != 0) { + copyCount = n; + } else { + copyCount = 2; + } + n -= copyCount; + copyCount /= sizeof(uint16_t); + + while (copyCount--) { + *reinterpret_cast<uint16_t*>(d) = *reinterpret_cast<const uint16_t*>(s); + d += sizeof(uint16_t); + s += sizeof(uint16_t); + } + } + + // Copy 32-bit aligned words. + copyCount = n / sizeof(uint32_t); + while (copyCount--) { + *reinterpret_cast<uint32_t*>(d) = *reinterpret_cast<const uint32_t*>(s); + d += sizeof(uint32_t); + s += sizeof(uint32_t); + } + + // Check for leftovers. Either we finished exactly, or we have one remaining 16-bit chunk. + if ((n & 0x02) != 0) { + *(uint16_t*)d = *(uint16_t*)s; + } + } else { + // Copy backward, starting at the end. + d += n; + s += n; + + if (((reinterpret_cast<uintptr_t>(d) | reinterpret_cast<uintptr_t>(s)) & 0x03) != 0) { + // try for 32-bit alignment. + if (((reinterpret_cast<uintptr_t>(d) ^ reinterpret_cast<uintptr_t>(s)) & 0x03) != 0) { + copyCount = n; + } else { + copyCount = 2; + } + n -= copyCount; + copyCount /= sizeof(uint16_t); + + while (copyCount--) { + d -= sizeof(uint16_t); + s -= sizeof(uint16_t); + *reinterpret_cast<uint16_t*>(d) = *reinterpret_cast<const uint16_t*>(s); + } + } + + // Copy 32-bit aligned words. + copyCount = n / sizeof(uint32_t); + while (copyCount--) { + d -= sizeof(uint32_t); + s -= sizeof(uint32_t); + *reinterpret_cast<uint32_t*>(d) = *reinterpret_cast<const uint32_t*>(s); + } + + // Copy leftovers. + if ((n & 0x02) != 0) { + d -= sizeof(uint16_t); + s -= sizeof(uint16_t); + *reinterpret_cast<uint16_t*>(d) = *reinterpret_cast<const uint16_t*>(s); + } + } +} + +#define move16 MemmoveWords +#define move32 MemmoveWords + +namespace art { + +static void ThrowArrayStoreException_NotAnArray(const char* identifier, mirror::Object* array) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::string actualType(PrettyTypeOf(array)); + Thread* self = Thread::Current(); + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;", + "%s of type %s is not an array", identifier, actualType.c_str()); +} + +static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) { + ScopedObjectAccess soa(env); + + // Null pointer checks. + if (UNLIKELY(javaSrc == NULL)) { + ThrowNullPointerException(NULL, "src == null"); + return; + } + if (UNLIKELY(javaDst == NULL)) { + ThrowNullPointerException(NULL, "dst == null"); + return; + } + + // Make sure source and destination are both arrays. + mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc); + mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst); + if (UNLIKELY(!srcObject->IsArrayInstance())) { + ThrowArrayStoreException_NotAnArray("source", srcObject); + return; + } + if (UNLIKELY(!dstObject->IsArrayInstance())) { + ThrowArrayStoreException_NotAnArray("destination", dstObject); + return; + } + mirror::Array* srcArray = srcObject->AsArray(); + mirror::Array* dstArray = dstObject->AsArray(); + mirror::Class* srcComponentType = srcArray->GetClass()->GetComponentType(); + mirror::Class* dstComponentType = dstArray->GetClass()->GetComponentType(); + + // Bounds checking. + if (UNLIKELY(srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length)) { + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;", + "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d", + srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length); + return; + } + + // Handle primitive arrays. + if (srcComponentType->IsPrimitive() || dstComponentType->IsPrimitive()) { + // If one of the arrays holds a primitive type the other array must hold the exact same type. + if (UNLIKELY(srcComponentType != dstComponentType)) { + std::string srcType(PrettyTypeOf(srcArray)); + std::string dstType(PrettyTypeOf(dstArray)); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;", + "Incompatible types: src=%s, dst=%s", + srcType.c_str(), dstType.c_str()); + return; + } + + size_t width = srcArray->GetClass()->GetComponentSize(); + uint8_t* dstBytes = reinterpret_cast<uint8_t*>(dstArray->GetRawData(width)); + const uint8_t* srcBytes = reinterpret_cast<const uint8_t*>(srcArray->GetRawData(width)); + + switch (width) { + case 1: + memmove(dstBytes + dstPos, srcBytes + srcPos, length); + break; + case 2: + move16(dstBytes + dstPos * 2, srcBytes + srcPos * 2, length * 2); + break; + case 4: + move32(dstBytes + dstPos * 4, srcBytes + srcPos * 4, length * 4); + break; + case 8: + // We don't need to guarantee atomicity of the entire 64-bit word. + move32(dstBytes + dstPos * 8, srcBytes + srcPos * 8, length * 8); + break; + default: + LOG(FATAL) << "Unknown primitive array type: " << PrettyTypeOf(srcArray); + } + + return; + } + + // Neither class is primitive. Are the types trivially compatible? + const size_t width = sizeof(mirror::Object*); + uint8_t* dstBytes = reinterpret_cast<uint8_t*>(dstArray->GetRawData(width)); + const uint8_t* srcBytes = reinterpret_cast<const uint8_t*>(srcArray->GetRawData(width)); + if (dstArray == srcArray || dstComponentType->IsAssignableFrom(srcComponentType)) { + // Yes. Bulk copy. + COMPILE_ASSERT(sizeof(width) == sizeof(uint32_t), move32_assumes_Object_references_are_32_bit); + move32(dstBytes + dstPos * width, srcBytes + srcPos * width, length * width); + Runtime::Current()->GetHeap()->WriteBarrierArray(dstArray, dstPos, length); + return; + } + + // The arrays are not trivially compatible. However, we may still be able to copy some or all of + // the elements if the source objects are compatible (for example, copying an Object[] to + // String[], the Objects being copied might actually be Strings). + // We can't do a bulk move because that would introduce a check-use race condition, so we copy + // elements one by one. + + // We already dealt with overlapping copies, so we don't need to cope with that case below. + CHECK_NE(dstArray, srcArray); + + mirror::Object* const * srcObjects = + reinterpret_cast<mirror::Object* const *>(srcBytes + srcPos * width); + mirror::Object** dstObjects = reinterpret_cast<mirror::Object**>(dstBytes + dstPos * width); + mirror::Class* dstClass = dstArray->GetClass()->GetComponentType(); + + // We want to avoid redundant IsAssignableFrom checks where possible, so we cache a class that + // we know is assignable to the destination array's component type. + mirror::Class* lastAssignableElementClass = dstClass; + + mirror::Object* o = NULL; + int i = 0; + for (; i < length; ++i) { + o = srcObjects[i]; + if (o != NULL) { + mirror::Class* oClass = o->GetClass(); + if (lastAssignableElementClass == oClass) { + dstObjects[i] = o; + } else if (dstClass->IsAssignableFrom(oClass)) { + lastAssignableElementClass = oClass; + dstObjects[i] = o; + } else { + // Can't put this element into the array. + break; + } + } else { + dstObjects[i] = NULL; + } + } + + Runtime::Current()->GetHeap()->WriteBarrierArray(dstArray, dstPos, length); + if (UNLIKELY(i != length)) { + std::string actualSrcType(PrettyTypeOf(o)); + std::string dstType(PrettyTypeOf(dstArray)); + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;", + "source[%d] of type %s cannot be stored in destination array of type %s", + srcPos + i, actualSrcType.c_str(), dstType.c_str()); + return; + } +} + +static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { + ScopedObjectAccess soa(env); + mirror::Object* o = soa.Decode<mirror::Object*>(javaObject); + return static_cast<jint>(o->IdentityHashCode()); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"), + NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"), +}; + +void register_java_lang_System(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/System"); +} + +} // namespace art diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc new file mode 100644 index 0000000000..8ef190aa3f --- /dev/null +++ b/runtime/native/java_lang_Thread.cc @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2008 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_throws.h" +#include "debugger.h" +#include "jni_internal.h" +#include "monitor.h" +#include "mirror/object.h" +#include "scoped_thread_state_change.h" +#include "ScopedUtfChars.h" +#include "thread.h" +#include "thread_list.h" + +namespace art { + +static jobject Thread_currentThread(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + return soa.AddLocalReference<jobject>(soa.Self()->GetPeer()); +} + +static jboolean Thread_interrupted(JNIEnv* env, jclass) { + return static_cast<JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; +} + +static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) { + ScopedObjectAccess soa(env); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* thread = Thread::FromManagedThread(soa, java_thread); + return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE; +} + +static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size, + jboolean daemon) { + Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE); +} + +static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean has_been_started) { + // Ordinals from Java's Thread.State. + const jint kJavaNew = 0; + const jint kJavaRunnable = 1; + const jint kJavaBlocked = 2; + const jint kJavaWaiting = 3; + const jint kJavaTimedWaiting = 4; + const jint kJavaTerminated = 5; + + ScopedObjectAccess soa(env); + ThreadState internal_thread_state = (has_been_started ? kTerminated : kStarting); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* thread = Thread::FromManagedThread(soa, java_thread); + if (thread != NULL) { + internal_thread_state = thread->GetState(); + } + switch (internal_thread_state) { + case kTerminated: return kJavaTerminated; + case kRunnable: return kJavaRunnable; + case kTimedWaiting: return kJavaTimedWaiting; + case kSleeping: return kJavaTimedWaiting; + case kBlocked: return kJavaBlocked; + case kWaiting: return kJavaWaiting; + case kStarting: return kJavaNew; + case kNative: return kJavaRunnable; + case kWaitingForGcToComplete: return kJavaWaiting; + case kWaitingPerformingGc: return kJavaWaiting; + case kWaitingForCheckPointsToRun: return kJavaWaiting; + case kWaitingForDebuggerSend: return kJavaWaiting; + case kWaitingForDebuggerToAttach: return kJavaWaiting; + case kWaitingInMainDebuggerLoop: return kJavaWaiting; + case kWaitingForDebuggerSuspension: return kJavaWaiting; + case kWaitingForJniOnLoad: return kJavaWaiting; + case kWaitingForSignalCatcherOutput: return kJavaWaiting; + case kWaitingInMainSignalCatcherLoop: return kJavaWaiting; + case kSuspended: return kJavaRunnable; + // Don't add a 'default' here so the compiler can spot incompatible enum changes. + } + return -1; // Unreachable. +} + +static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) { + ScopedObjectAccess soa(env); + mirror::Object* object = soa.Decode<mirror::Object*>(java_object); + if (object == NULL) { + ThrowNullPointerException(NULL, "object == null"); + return JNI_FALSE; + } + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* thread = Thread::FromManagedThread(soa, java_thread); + return thread->HoldsLock(object); +} + +static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) { + ScopedObjectAccess soa(env); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* thread = Thread::FromManagedThread(soa, java_thread); + if (thread != NULL) { + thread->Interrupt(); + } +} + +static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) { + ScopedUtfChars name(env, java_name); + { + ScopedObjectAccess soa(env); + if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) { + soa.Self()->SetThreadName(name.c_str()); + return; + } + } + // Suspend thread to avoid it from killing itself while we set its name. We don't just hold the + // thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock + // in the DDMS send code. + bool timed_out; + Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out); + if (thread != NULL) { + { + ScopedObjectAccess soa(env); + thread->SetThreadName(name.c_str()); + } + Runtime::Current()->GetThreadList()->Resume(thread, true); + } else if (timed_out) { + LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread " + "failed to suspend within a generous timeout."; + } +} + +/* + * Alter the priority of the specified thread. "new_priority" will range + * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal" + * threads at Thread.NORM_PRIORITY (5). + */ +static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_priority) { + ScopedObjectAccess soa(env); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* thread = Thread::FromManagedThread(soa, java_thread); + if (thread != NULL) { + thread->SetNativePriority(new_priority); + } +} + +static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) { + ScopedObjectAccess soa(env); + mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock); + Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping); +} + +/* + * Causes the thread to temporarily pause and allow other threads to execute. + * + * The exact behavior is poorly defined. Some discussion here: + * http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html + */ +static void Thread_yield(JNIEnv*, jobject) { + sched_yield(); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"), + NATIVE_METHOD(Thread, interrupted, "()Z"), + NATIVE_METHOD(Thread, isInterrupted, "()Z"), + NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"), + NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"), + NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Thread, nativeInterrupt, "()V"), + NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"), + NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"), + NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Thread, yield, "()V"), +}; + +void register_java_lang_Thread(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/Thread"); +} + +} // namespace art diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc new file mode 100644 index 0000000000..332a1305e0 --- /dev/null +++ b/runtime/native/java_lang_Throwable.cc @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 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 "jni_internal.h" +#include "scoped_thread_state_change.h" +#include "thread.h" + +namespace art { + +static jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + return soa.Self()->CreateInternalStackTrace(soa); +} + +static jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject javaStackState) { + if (javaStackState == NULL) { + return NULL; + } + return Thread::InternalStackTraceToStackTraceElementArray(env, javaStackState); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Throwable, nativeFillInStackTrace, "()Ljava/lang/Object;"), + NATIVE_METHOD(Throwable, nativeGetStackTrace, "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;"), +}; + +void register_java_lang_Throwable(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/Throwable"); +} + +} // namespace art diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc new file mode 100644 index 0000000000..c23b08ca66 --- /dev/null +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "jni_internal.h" +#include "mirror/class_loader.h" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" +#include "ScopedUtfChars.h" +#include "zip_archive.h" + +namespace art { + +static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) { + ScopedObjectAccess soa(env); + mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader); + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + + std::string descriptor(DotToDescriptor(name.c_str())); + mirror::Class* c = Runtime::Current()->GetClassLinker()->LookupClass(descriptor.c_str(), loader); + if (c != NULL && c->IsResolved()) { + return soa.AddLocalReference<jclass>(c); + } else { + // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into + // the regular loadClass code. + return NULL; + } +} + +static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) { + return Runtime::Current()->GetClassLinker()->GetBootClassPath().size(); +} + +/* + * Returns a string URL for a resource with the specified 'javaName' in + * entry 'index' of the boot class path. + * + * We return a newly-allocated String in the following form: + * + * jar:file://path!/name + * + * Where "path" is the bootstrap class path entry and "name" is the string + * passed into this method. "path" needs to be an absolute path (starting + * with '/'); if it's not we'd need to make it absolute as part of forming + * the URL string. + */ +static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName, jint index) { + ScopedUtfChars name(env, javaName); + if (name.c_str() == NULL) { + return NULL; + } + + const std::vector<const DexFile*>& path = Runtime::Current()->GetClassLinker()->GetBootClassPath(); + if (index < 0 || size_t(index) >= path.size()) { + return NULL; + } + const DexFile* dex_file = path[index]; + const std::string& location(dex_file->GetLocation()); + UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(location)); + if (zip_archive.get() == NULL) { + return NULL; + } + UniquePtr<ZipEntry> zip_entry(zip_archive->Find(name.c_str())); + if (zip_entry.get() == NULL) { + return NULL; + } + + std::string url; + StringAppendF(&url, "jar:file://%s!/%s", location.c_str(), name.c_str()); + return env->NewStringUTF(url.c_str()); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(VMClassLoader, findLoadedClass, "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"), + NATIVE_METHOD(VMClassLoader, getBootClassPathResource, "(Ljava/lang/String;I)Ljava/lang/String;"), + NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "()I"), +}; + +void register_java_lang_VMClassLoader(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/VMClassLoader"); +} + +} // namespace art diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc new file mode 100644 index 0000000000..45ec0ad5a2 --- /dev/null +++ b/runtime/native/java_lang_reflect_Array.cc @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "common_throws.h" +#include "dex_file-inl.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "scoped_thread_state_change.h" +#include "sirt_ref.h" + +namespace art { + +static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) { + ScopedObjectAccess soa(env); + DCHECK(javaElementClass != NULL); + mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); + DCHECK(element_class->IsClass()); + DCHECK(javaDimArray != NULL); + mirror::Object* dimensions_obj = soa.Decode<mirror::Object*>(javaDimArray); + DCHECK(dimensions_obj->IsArrayInstance()); + DCHECK_STREQ(ClassHelper(dimensions_obj->GetClass()).GetDescriptor(), "[I"); + mirror::IntArray* dimensions_array = down_cast<mirror::IntArray*>(dimensions_obj); + mirror::Array* new_array = mirror::Array::CreateMultiArray(soa.Self(), element_class, dimensions_array); + return soa.AddLocalReference<jobject>(new_array); +} + +static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) { + ScopedObjectAccess soa(env); + DCHECK(javaElementClass != NULL); + mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); + if (UNLIKELY(length < 0)) { + ThrowNegativeArraySizeException(length); + return NULL; + } + std::string descriptor("["); + descriptor += ClassHelper(element_class).GetDescriptor(); + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader()); + if (UNLIKELY(array_class == NULL)) { + CHECK(soa.Self()->IsExceptionPending()); + return NULL; + } + DCHECK(array_class->IsArrayClass()); + mirror::Array* new_array = mirror::Array::Alloc(soa.Self(), array_class, length); + return soa.AddLocalReference<jobject>(new_array); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Array, createMultiArray, "(Ljava/lang/Class;[I)Ljava/lang/Object;"), + NATIVE_METHOD(Array, createObjectArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), +}; + +void register_java_lang_reflect_Array(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/reflect/Array"); +} + +} // namespace art diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc new file mode 100644 index 0000000000..918021748b --- /dev/null +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "mirror/abstract_method.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "reflection.h" +#include "scoped_thread_state_change.h" + +namespace art { + +/* + * We get here through Constructor.newInstance(). The Constructor object + * would not be available if the constructor weren't public (per the + * definition of Class.getConstructor), so we can skip the method access + * check. We can also safely assume the constructor isn't associated + * with an interface, array, or primitive class. + */ +static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { + ScopedObjectAccess soa(env); + mirror::AbstractMethod* m = soa.Decode<mirror::Object*>(javaMethod)->AsMethod(); + mirror::Class* c = m->GetDeclaringClass(); + if (UNLIKELY(c->IsAbstract())) { + ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); + soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/InstantiationException;", + "Can't instantiate %s %s", + c->IsInterface() ? "interface" : "abstract class", + PrettyDescriptor(c).c_str()); + return NULL; + } + + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { + DCHECK(soa.Self()->IsExceptionPending()); + return NULL; + } + + mirror::Object* receiver = c->AllocObject(soa.Self()); + if (receiver == NULL) { + return NULL; + } + + jobject javaReceiver = soa.AddLocalReference<jobject>(receiver); + InvokeMethod(soa, javaMethod, javaReceiver, javaArgs); + + // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. + return javaReceiver; +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Constructor, newInstance, "([Ljava/lang/Object;)Ljava/lang/Object;"), +}; + +void register_java_lang_reflect_Constructor(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/reflect/Constructor"); +} + +} // namespace art diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc new file mode 100644 index 0000000000..b0daa916c6 --- /dev/null +++ b/runtime/native/java_lang_reflect_Field.cc @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "class_linker-inl.h" +#include "common_throws.h" +#include "dex_file-inl.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "mirror/field.h" +#include "mirror/field-inl.h" +#include "object_utils.h" +#include "reflection.h" +#include "scoped_thread_state_change.h" + +namespace art { + +static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirror::Field* f, + JValue& value, bool allow_references) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK_EQ(value.GetJ(), 0LL); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), + true, true)) { + return false; + } + switch (FieldHelper(f).GetTypeAsPrimitiveType()) { + case Primitive::kPrimBoolean: + value.SetZ(f->GetBoolean(o)); + return true; + case Primitive::kPrimByte: + value.SetB(f->GetByte(o)); + return true; + case Primitive::kPrimChar: + value.SetC(f->GetChar(o)); + return true; + case Primitive::kPrimDouble: + value.SetD(f->GetDouble(o)); + return true; + case Primitive::kPrimFloat: + value.SetF(f->GetFloat(o)); + return true; + case Primitive::kPrimInt: + value.SetI(f->GetInt(o)); + return true; + case Primitive::kPrimLong: + value.SetJ(f->GetLong(o)); + return true; + case Primitive::kPrimShort: + value.SetS(f->GetShort(o)); + return true; + case Primitive::kPrimNot: + if (allow_references) { + value.SetL(f->GetObject(o)); + return true; + } + // Else break to report an error. + break; + case Primitive::kPrimVoid: + // Never okay. + break; + } + ThrowIllegalArgumentException(NULL, + StringPrintf("Not a primitive field: %s", + PrettyField(f).c_str()).c_str()); + return false; +} + +static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::Field* f, + mirror::Object*& class_or_rcvr) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (f->IsStatic()) { + class_or_rcvr = f->GetDeclaringClass(); + return true; + } + + class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr); + mirror::Class* declaringClass = f->GetDeclaringClass(); + if (!VerifyObjectInClass(class_or_rcvr, declaringClass)) { + return false; + } + return true; +} + +static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) { + ScopedObjectAccess soa(env); + mirror::Field* f = soa.DecodeField(env->FromReflectedField(javaField)); + mirror::Object* o = NULL; + if (!CheckReceiver(soa, javaObj, f, o)) { + return NULL; + } + + // Get the field's value, boxing if necessary. + JValue value; + if (!GetFieldValue(soa, o, f, value, true)) { + return NULL; + } + return + soa.AddLocalReference<jobject>(BoxPrimitive(FieldHelper(f).GetTypeAsPrimitiveType(), value)); +} + +static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char dst_descriptor) { + ScopedObjectAccess soa(env); + mirror::Field* f = soa.DecodeField(env->FromReflectedField(javaField)); + mirror::Object* o = NULL; + if (!CheckReceiver(soa, javaObj, f, o)) { + return JValue(); + } + + // Read the value. + JValue field_value; + if (!GetFieldValue(soa, o, f, field_value, false)) { + return JValue(); + } + + // Widen it if necessary (and possible). + JValue wide_value; + mirror::Class* dst_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(dst_descriptor); + if (!ConvertPrimitiveValue(NULL, false, FieldHelper(f).GetTypeAsPrimitiveType(), + dst_type->GetPrimitiveType(), field_value, wide_value)) { + return JValue(); + } + return wide_value; +} + +static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'Z').GetZ(); +} + +static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'B').GetB(); +} + +static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'C').GetC(); +} + +static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'D').GetD(); +} + +static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'F').GetF(); +} + +static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'I').GetI(); +} + +static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'J').GetJ(); +} + +static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField(env, javaField, javaObj, 'S').GetS(); +} + +static void SetFieldValue(mirror::Object* o, mirror::Field* f, const JValue& new_value, + bool allow_references) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), + true, true)) { + return; + } + switch (FieldHelper(f).GetTypeAsPrimitiveType()) { + case Primitive::kPrimBoolean: + f->SetBoolean(o, new_value.GetZ()); + break; + case Primitive::kPrimByte: + f->SetByte(o, new_value.GetB()); + break; + case Primitive::kPrimChar: + f->SetChar(o, new_value.GetC()); + break; + case Primitive::kPrimDouble: + f->SetDouble(o, new_value.GetD()); + break; + case Primitive::kPrimFloat: + f->SetFloat(o, new_value.GetF()); + break; + case Primitive::kPrimInt: + f->SetInt(o, new_value.GetI()); + break; + case Primitive::kPrimLong: + f->SetLong(o, new_value.GetJ()); + break; + case Primitive::kPrimShort: + f->SetShort(o, new_value.GetS()); + break; + case Primitive::kPrimNot: + if (allow_references) { + f->SetObject(o, new_value.GetL()); + break; + } + // Else fall through to report an error. + case Primitive::kPrimVoid: + // Never okay. + ThrowIllegalArgumentException(NULL, StringPrintf("Not a primitive field: %s", + PrettyField(f).c_str()).c_str()); + return; + } + + // Special handling for final fields on SMP systems. + // We need a store/store barrier here (JMM requirement). + if (f->IsFinal()) { + ANDROID_MEMBAR_STORE(); + } +} + +static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) { + ScopedObjectAccess soa(env); + mirror::Field* f = soa.DecodeField(env->FromReflectedField(javaField)); + + // Unbox the value, if necessary. + mirror::Object* boxed_value = soa.Decode<mirror::Object*>(javaValue); + JValue unboxed_value; + if (!UnboxPrimitiveForField(boxed_value, FieldHelper(f).GetType(), unboxed_value, f)) { + return; + } + + // Check that the receiver is non-null and an instance of the field's declaring class. + mirror::Object* o = NULL; + if (!CheckReceiver(soa, javaObj, f, o)) { + return; + } + + SetFieldValue(o, f, unboxed_value, true); +} + +static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor, + const JValue& new_value) { + ScopedObjectAccess soa(env); + mirror::Field* f = soa.DecodeField(env->FromReflectedField(javaField)); + mirror::Object* o = NULL; + if (!CheckReceiver(soa, javaObj, f, o)) { + return; + } + FieldHelper fh(f); + if (!fh.IsPrimitiveType()) { + ThrowIllegalArgumentException(NULL, StringPrintf("Not a primitive field: %s", + PrettyField(f).c_str()).c_str()); + return; + } + + // Widen the value if necessary (and possible). + JValue wide_value; + mirror::Class* src_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(src_descriptor); + if (!ConvertPrimitiveValue(NULL, false, src_type->GetPrimitiveType(), fh.GetTypeAsPrimitiveType(), + new_value, wide_value)) { + return; + } + + // Write the value. + SetFieldValue(o, f, wide_value, false); +} + +static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z) { + JValue value; + value.SetZ(z); + SetPrimitiveField(env, javaField, javaObj, 'Z', value); +} + +static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b) { + JValue value; + value.SetB(b); + SetPrimitiveField(env, javaField, javaObj, 'B', value); +} + +static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c) { + JValue value; + value.SetC(c); + SetPrimitiveField(env, javaField, javaObj, 'C', value); +} + +static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d) { + JValue value; + value.SetD(d); + SetPrimitiveField(env, javaField, javaObj, 'D', value); +} + +static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f) { + JValue value; + value.SetF(f); + SetPrimitiveField(env, javaField, javaObj, 'F', value); +} + +static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i) { + JValue value; + value.SetI(i); + SetPrimitiveField(env, javaField, javaObj, 'I', value); +} + +static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j) { + JValue value; + value.SetJ(j); + SetPrimitiveField(env, javaField, javaObj, 'J', value); +} + +static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s) { + JValue value; + value.SetS(s); + SetPrimitiveField(env, javaField, javaObj, 'S', value); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Field, get, "(Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(Field, getBoolean, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Field, getByte, "(Ljava/lang/Object;)B"), + NATIVE_METHOD(Field, getChar, "(Ljava/lang/Object;)C"), + NATIVE_METHOD(Field, getDouble, "(Ljava/lang/Object;)D"), + NATIVE_METHOD(Field, getFloat, "(Ljava/lang/Object;)F"), + NATIVE_METHOD(Field, getInt, "(Ljava/lang/Object;)I"), + NATIVE_METHOD(Field, getLong, "(Ljava/lang/Object;)J"), + NATIVE_METHOD(Field, getShort, "(Ljava/lang/Object;)S"), + NATIVE_METHOD(Field, set, "(Ljava/lang/Object;Ljava/lang/Object;)V"), + NATIVE_METHOD(Field, setBoolean, "(Ljava/lang/Object;Z)V"), + NATIVE_METHOD(Field, setByte, "(Ljava/lang/Object;B)V"), + NATIVE_METHOD(Field, setChar, "(Ljava/lang/Object;C)V"), + NATIVE_METHOD(Field, setDouble, "(Ljava/lang/Object;D)V"), + NATIVE_METHOD(Field, setFloat, "(Ljava/lang/Object;F)V"), + NATIVE_METHOD(Field, setInt, "(Ljava/lang/Object;I)V"), + NATIVE_METHOD(Field, setLong, "(Ljava/lang/Object;J)V"), + NATIVE_METHOD(Field, setShort, "(Ljava/lang/Object;S)V"), +}; + +void register_java_lang_reflect_Field(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/reflect/Field"); +} + +} // namespace art diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc new file mode 100644 index 0000000000..14dc6a44ee --- /dev/null +++ b/runtime/native/java_lang_reflect_Method.cc @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "mirror/abstract_method.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/proxy.h" +#include "object_utils.h" +#include "reflection.h" +#include "scoped_thread_state_change.h" + +namespace art { + +static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver, jobject javaArgs) { + ScopedObjectAccess soa(env); + return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs); +} + +static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) { + ScopedObjectAccess soa(env); + mirror::AbstractMethod* proxy_method = soa.Decode<mirror::Object*>(javaMethod)->AsMethod(); + CHECK(proxy_method->GetDeclaringClass()->IsProxyClass()); + mirror::SynthesizedProxyClass* proxy_class = + down_cast<mirror::SynthesizedProxyClass*>(proxy_method->GetDeclaringClass()); + int throws_index = -1; + size_t num_virt_methods = proxy_class->NumVirtualMethods(); + for (size_t i = 0; i < num_virt_methods; i++) { + if (proxy_class->GetVirtualMethod(i) == proxy_method) { + throws_index = i; + break; + } + } + CHECK_NE(throws_index, -1); + mirror::ObjectArray<mirror::Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index); + return soa.AddLocalReference<jobject>(declared_exceptions->Clone(soa.Self())); +} + +static jobject Method_findOverriddenMethodNative(JNIEnv* env, jobject javaMethod) { + ScopedObjectAccess soa(env); + mirror::AbstractMethod* method = soa.Decode<mirror::Object*>(javaMethod)->AsMethod(); + return soa.AddLocalReference<jobject>(method->FindOverriddenMethod()); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Method, invoke, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(Method, getExceptionTypesNative, "()[Ljava/lang/Class;"), + NATIVE_METHOD(Method, findOverriddenMethodNative, "()Ljava/lang/reflect/Method;"), +}; + +void register_java_lang_reflect_Method(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/reflect/Method"); +} + +} // namespace art diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc new file mode 100644 index 0000000000..547ce7b38d --- /dev/null +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 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 "class_linker.h" +#include "jni_internal.h" +#include "mirror/class_loader.h" +#include "mirror/object_array.h" +#include "mirror/string.h" +#include "scoped_thread_state_change.h" + +namespace art { + +static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring javaName, + jobjectArray javaInterfaces, jobject javaLoader, + jobjectArray javaMethods, jobjectArray javaThrows) { + ScopedObjectAccess soa(env); + mirror::String* name = soa.Decode<mirror::String*>(javaName); + mirror::ObjectArray<mirror::Class>* interfaces = + soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaInterfaces); + mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader); + mirror::ObjectArray<mirror::AbstractMethod>* methods = + soa.Decode<mirror::ObjectArray<mirror::AbstractMethod>*>(javaMethods); + mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >* throws = + soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(javaThrows); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws); + return soa.AddLocalReference<jclass>(result); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Proxy, generateProxy, "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;)Ljava/lang/Class;"), +}; + +void register_java_lang_reflect_Proxy(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/reflect/Proxy"); +} + +} // namespace art diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc new file mode 100644 index 0000000000..bf92e1230d --- /dev/null +++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 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 "atomic.h" +#include "jni_internal.h" + +namespace art { + +static jboolean AtomicLong_VMSupportsCS8(JNIEnv*, jclass) { + return QuasiAtomic::LongAtomicsUseMutexes() ? JNI_FALSE : JNI_TRUE; +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(AtomicLong, VMSupportsCS8, "()Z"), +}; + +void register_java_util_concurrent_atomic_AtomicLong(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/util/concurrent/atomic/AtomicLong"); +} + +} // namespace art diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc new file mode 100644 index 0000000000..d7cd18dc9c --- /dev/null +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 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 "base/logging.h" +#include "debugger.h" +#include "scoped_thread_state_change.h" +#include "ScopedPrimitiveArray.h" + +namespace art { + +static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type, + jbyteArray javaData, jint offset, jint length) { + ScopedObjectAccess soa(env); + ScopedByteArrayRO data(env, javaData); + DCHECK_LE(offset + length, static_cast<int32_t>(data.size())); + Dbg::DdmSendChunk(type, length, reinterpret_cast<const uint8_t*>(&data[offset])); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(DdmServer, nativeSendChunk, "(I[BII)V"), +}; + +void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) { + REGISTER_NATIVE_METHODS("org/apache/harmony/dalvik/ddmc/DdmServer"); +} + +} // namespace art diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc new file mode 100644 index 0000000000..5ba29946d1 --- /dev/null +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2008 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 "base/logging.h" +#include "base/mutex.h" +#include "debugger.h" +#include "jni_internal.h" +#include "scoped_thread_state_change.h" +#include "ScopedLocalRef.h" +#include "ScopedPrimitiveArray.h" +#include "stack.h" +#include "thread_list.h" + +namespace art { + +static void DdmVmInternal_enableRecentAllocations(JNIEnv*, jclass, jboolean enable) { + Dbg::SetAllocTrackingEnabled(enable); +} + +static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + return Dbg::GetRecentAllocations(); +} + +static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) { + return Dbg::IsAllocTrackingEnabled(); +} + +/* + * Get a stack trace as an array of StackTraceElement objects. Returns + * NULL on failure, e.g. if the threadId couldn't be found. + */ +static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) { + ScopedLocalRef<jobject> peer(env, NULL); + { + Thread* t = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(thin_lock_id); + if (t == NULL) { + return NULL; + } + ScopedObjectAccess soa(env); + peer.reset(soa.AddLocalReference<jobject>(t->GetPeer())); + } + if (peer.get() == NULL) { + return NULL; + } + + // Suspend thread to build stack trace. + bool timed_out; + Thread* thread = Thread::SuspendForDebugger(peer.get(), true, &timed_out); + if (thread != NULL) { + jobject trace; + { + ScopedObjectAccess soa(env); + trace = thread->CreateInternalStackTrace(soa); + } + // Restart suspended thread. + Runtime::Current()->GetThreadList()->Resume(thread, true); + return Thread::InternalStackTraceToStackTraceElementArray(env, trace); + } else { + if (timed_out) { + LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend " + "within a generous timeout."; + } + return NULL; + } +} + +static void ThreadCountCallback(Thread*, void* context) { + uint16_t& count = *reinterpret_cast<uint16_t*>(context); + ++count; +} + +static const int kThstBytesPerEntry = 18; +static const int kThstHeaderLen = 4; + +static void ThreadStatsGetterCallback(Thread* t, void* context) { + /* + * Generate the contents of a THST chunk. The data encompasses all known + * threads. + * + * Response has: + * (1b) header len + * (1b) bytes per entry + * (2b) thread count + * Then, for each thread: + * (4b) thread id + * (1b) thread status + * (4b) tid + * (4b) utime + * (4b) stime + * (1b) is daemon? + * + * The length fields exist in anticipation of adding additional fields + * without wanting to break ddms or bump the full protocol version. I don't + * think it warrants full versioning. They might be extraneous and could + * be removed from a future version. + */ + char native_thread_state; + int utime, stime, task_cpu; + GetTaskStats(t->GetTid(), native_thread_state, utime, stime, task_cpu); + + std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context); + JDWP::Append4BE(bytes, t->GetThinLockId()); + JDWP::Append1BE(bytes, t->GetState()); + JDWP::Append4BE(bytes, t->GetTid()); + JDWP::Append4BE(bytes, utime); + JDWP::Append4BE(bytes, stime); + JDWP::Append1BE(bytes, t->IsDaemon()); +} + +static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) { + std::vector<uint8_t> bytes; + Thread* self = static_cast<JNIEnvExt*>(env)->self; + { + MutexLock mu(self, *Locks::thread_list_lock_); + ThreadList* thread_list = Runtime::Current()->GetThreadList(); + + uint16_t thread_count = 0; + thread_list->ForEach(ThreadCountCallback, &thread_count); + + JDWP::Append1BE(bytes, kThstHeaderLen); + JDWP::Append1BE(bytes, kThstBytesPerEntry); + JDWP::Append2BE(bytes, thread_count); + + thread_list->ForEach(ThreadStatsGetterCallback, &bytes); + } + + jbyteArray result = env->NewByteArray(bytes.size()); + if (result != NULL) { + env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0])); + } + return result; +} + +static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) { + ScopedObjectAccess soa(env); + return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when)); +} + +static jboolean DdmVmInternal_heapSegmentNotify(JNIEnv*, jclass, jint when, jint what, jboolean native) { + return Dbg::DdmHandleHpsgNhsgChunk(static_cast<Dbg::HpsgWhen>(when), static_cast<Dbg::HpsgWhat>(what), native); +} + +static void DdmVmInternal_threadNotify(JNIEnv*, jclass, jboolean enable) { + Dbg::DdmSetThreadNotification(enable); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(DdmVmInternal, enableRecentAllocations, "(Z)V"), + NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "()[B"), + NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "()Z"), + NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"), + NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"), + NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "(I)Z"), + NATIVE_METHOD(DdmVmInternal, heapSegmentNotify, "(IIZ)Z"), + NATIVE_METHOD(DdmVmInternal, threadNotify, "(Z)V"), +}; + +void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env) { + REGISTER_NATIVE_METHODS("org/apache/harmony/dalvik/ddmc/DdmVmInternal"); +} + +} // namespace art diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc new file mode 100644 index 0000000000..eece81a9e8 --- /dev/null +++ b/runtime/native/sun_misc_Unsafe.cc @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008 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 "atomic.h" +#include "gc/accounting/card_table-inl.h" +#include "jni_internal.h" +#include "mirror/object.h" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" + +namespace art { + +static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + byte* raw_addr = reinterpret_cast<byte*>(obj) + offset; + volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr); + // Note: android_atomic_release_cas() returns 0 on success, not failure. + int result = android_atomic_release_cas(expectedValue, newValue, address); + return (result == 0) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + byte* raw_addr = reinterpret_cast<byte*>(obj) + offset; + volatile int64_t* address = reinterpret_cast<volatile int64_t*>(raw_addr); + // Note: android_atomic_cmpxchg() returns 0 on success, not failure. + bool success = QuasiAtomic::Cas64(expectedValue, newValue, address); + return success ? JNI_TRUE : JNI_FALSE; +} + +static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue); + mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); + byte* raw_addr = reinterpret_cast<byte*>(obj) + offset; + int32_t* address = reinterpret_cast<int32_t*>(raw_addr); + // Note: android_atomic_cmpxchg() returns 0 on success, not failure. + int result = android_atomic_release_cas(reinterpret_cast<int32_t>(expectedValue), + reinterpret_cast<int32_t>(newValue), address); + if (result == 0) { + Runtime::Current()->GetHeap()->WriteBarrierField(obj, MemberOffset(offset), newValue); + } + return (result == 0) ? JNI_TRUE : JNI_FALSE; +} + +static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + return obj->GetField32(MemberOffset(offset), false); +} + +static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + return obj->GetField32(MemberOffset(offset), true); +} + +static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + obj->SetField32(MemberOffset(offset), newValue, false); +} + +static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + obj->SetField32(MemberOffset(offset), newValue, true); +} + +static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + ANDROID_MEMBAR_STORE(); + obj->SetField32(MemberOffset(offset), newValue, false); +} + +static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + return obj->GetField64(MemberOffset(offset), false); +} + +static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + return obj->GetField64(MemberOffset(offset), true); +} + +static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + obj->SetField64(MemberOffset(offset), newValue, false); +} + +static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + obj->SetField64(MemberOffset(offset), newValue, true); +} + +static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + ANDROID_MEMBAR_STORE(); + obj->SetField64(MemberOffset(offset), newValue, false); +} + +static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), true); + return soa.AddLocalReference<jobject>(value); +} + +static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), false); + return soa.AddLocalReference<jobject>(value); +} + +static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); + obj->SetFieldObject(MemberOffset(offset), newValue, false); +} + +static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); + obj->SetFieldObject(MemberOffset(offset), newValue, true); +} + +static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) { + ScopedObjectAccess soa(env); + mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); + mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); + ANDROID_MEMBAR_STORE(); + obj->SetFieldObject(MemberOffset(offset), newValue, false); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"), + NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"), + NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"), + NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"), + NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"), + NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"), + NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"), + NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"), + NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"), + NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"), + NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"), + NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"), + NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"), +}; + +void register_sun_misc_Unsafe(JNIEnv* env) { + REGISTER_NATIVE_METHODS("sun/misc/Unsafe"); +} + +} // namespace art |