diff options
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 0000000..b9838f8 --- /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 0000000..992998e --- /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 0000000..ce3cc93 --- /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 0000000..1a80d62 --- /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 0000000..9b995f4 --- /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 0000000..a729699 --- /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 0000000..5db7a33 --- /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 0000000..e380c17 --- /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 0000000..3e9c3f3 --- /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 0000000..2462f2f --- /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 0000000..8ef190a --- /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 0000000..332a130 --- /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 0000000..c23b08c --- /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 0000000..45ec0ad --- /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 0000000..9180217 --- /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 0000000..b0daa91 --- /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 0000000..14dc6a4 --- /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 0000000..547ce7b --- /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 0000000..bf92e12 --- /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 0000000..d7cd18d --- /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 0000000..5ba2994 --- /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 0000000..eece81a --- /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 |