summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/class_linker.cc6
-rw-r--r--src/class_linker.h2
-rw-r--r--src/class_linker_test.cc24
-rw-r--r--src/common_test.h26
-rw-r--r--src/dex_cache_test.cc2
-rw-r--r--src/dex_file.cc4
-rw-r--r--src/exception_test.cc2
-rw-r--r--src/file.h2
-rw-r--r--src/file_linux.h4
-rw-r--r--src/file_test.cc11
-rw-r--r--src/heap.cc45
-rw-r--r--src/heap.h14
-rw-r--r--src/image.cc10
-rw-r--r--src/image.h47
-rw-r--r--src/image_test.cc24
-rw-r--r--src/image_writer.cc12
-rw-r--r--src/image_writer.h6
-rw-r--r--src/indirect_reference_table.cc2
-rw-r--r--src/indirect_reference_table_test.cc2
-rw-r--r--src/intern_table_test.cc2
-rw-r--r--src/jni_compiler.cc4
-rw-r--r--src/jni_compiler_test.cc8
-rw-r--r--src/jni_internal.cc504
-rw-r--r--src/jni_internal.h32
-rw-r--r--src/jni_internal_test.cc4
-rw-r--r--src/mark_stack.cc9
-rw-r--r--src/mark_stack.h4
-rw-r--r--src/mark_sweep.cc2
-rw-r--r--src/mem_map.h63
-rw-r--r--src/object.h6
-rw-r--r--src/object_bitmap.cc3
-rw-r--r--src/object_test.cc2
-rw-r--r--src/os_linux.cc2
-rw-r--r--src/reference_table_test.cc2
-rw-r--r--src/runtime.cc163
-rw-r--r--src/runtime.h27
-rw-r--r--src/runtime_test.cc54
-rw-r--r--src/space.cc82
-rw-r--r--src/space.h37
-rw-r--r--src/space_test.cc8
-rw-r--r--src/thread.cc28
-rw-r--r--src/thread.h7
-rw-r--r--src/unordered_map.h1
-rw-r--r--src/unordered_set.h12
-rw-r--r--src/utils_test.cc2
-rw-r--r--src/zip_archive.cc1
-rw-r--r--src/zip_archive_test.cc2
47 files changed, 1018 insertions, 298 deletions
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 3437a5a8e0..a7d10f4a13 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -257,7 +257,7 @@ Method* ClassLinker::AllocMethod() {
return down_cast<Method*>(GetClassRoot(kJavaLangReflectMethod)->NewInstance());
}
-// TODO remove once we can use java.lang.Class.getSystemClassLoader
+// TODO: remove once we can use java.lang.Class.getSystemClassLoader
PathClassLoader* ClassLinker::AllocPathClassLoader(std::vector<const DexFile*> dex_files) {
PathClassLoader* cl = down_cast<PathClassLoader*>(GetClassRoot(kDalvikSystemPathClassLoader)->NewInstance());
cl->SetClassPath(dex_files);
@@ -266,7 +266,7 @@ PathClassLoader* ClassLinker::AllocPathClassLoader(std::vector<const DexFile*> d
Class* ClassLinker::FindClass(const StringPiece& descriptor,
ClassLoader* class_loader) {
- // TODO remove this contrived parent class loader check when we have a real ClassLoader.
+ // TODO: remove this contrived parent class loader check when we have a real ClassLoader.
if (class_loader != NULL) {
Class* klass = FindClass(descriptor, NULL);
if (klass != NULL) {
@@ -323,7 +323,7 @@ Class* ClassLinker::FindClass(const StringPiece& descriptor,
ObjectLock lock(klass);
klass->clinit_thread_id_ = self->GetId();
// Add the newly loaded class to the loaded classes table.
- bool success = InsertClass(klass); // TODO just return collision
+ bool success = InsertClass(klass); // TODO: just return collision
if (!success) {
// We may fail to insert if we raced with another thread.
klass->clinit_thread_id_ = 0;
diff --git a/src/class_linker.h b/src/class_linker.h
index b2b4c950d6..34ad9d7d3a 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -203,7 +203,7 @@ class ClassLinker {
bool init_done_;
- friend class RuntimeTest;
+ friend class CommonTest;
FRIEND_TEST(DexCacheTest, Open);
friend class ObjectTest;
FRIEND_TEST(ObjectTest, AllocObjectArray);
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 3b70aa928a..21c576ecf8 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -9,7 +9,7 @@
namespace art {
-class ClassLinkerTest : public RuntimeTest {
+class ClassLinkerTest : public CommonTest {
protected:
void AssertNonExistantClass(const StringPiece& descriptor) {
EXPECT_TRUE(class_linker_->FindSystemClass(descriptor) == NULL);
@@ -394,59 +394,59 @@ TEST_F(ClassLinkerTest, StaticFields) {
scoped_ptr<DexFile> dex(OpenDexFileBase64(kStatics));
PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
Class* statics = class_linker_->FindClass("LStatics;", class_loader);
- // class_linker_->InitializeClass(statics); // TODO uncomment this
+ // class_linker_->InitializeClass(statics); // TODO: uncomment this
EXPECT_EQ(10U, statics->NumStaticFields());
Field* s0 = statics->GetStaticField(0);
EXPECT_EQ("Ljava/lang/reflect/Field;", s0->GetClass()->descriptor_);
EXPECT_EQ('Z', s0->GetType());
-// EXPECT_EQ(true, s0->GetBoolean()); // TODO uncomment this
+// EXPECT_EQ(true, s0->GetBoolean()); // TODO: uncomment this
s0->SetBoolean(false);
Field* s1 = statics->GetStaticField(1);
EXPECT_EQ('B', s1->GetType());
-// EXPECT_EQ(5, s1->GetByte()); // TODO uncomment this
+// EXPECT_EQ(5, s1->GetByte()); // TODO: uncomment this
s1->SetByte(6);
Field* s2 = statics->GetStaticField(2);
EXPECT_EQ('C', s2->GetType());
-// EXPECT_EQ('a', s2->GetChar()); // TODO uncomment this
+// EXPECT_EQ('a', s2->GetChar()); // TODO: uncomment this
s2->SetChar('b');
Field* s3 = statics->GetStaticField(3);
EXPECT_EQ('S', s3->GetType());
-// EXPECT_EQ(65000, s3->GetShort()); // TODO uncomment this
+// EXPECT_EQ(65000, s3->GetShort()); // TODO: uncomment this
s3->SetShort(65001);
Field* s4 = statics->GetStaticField(4);
EXPECT_EQ('I', s4->GetType());
-// EXPECT_EQ(2000000000, s4->GetInt()); // TODO uncomment this
+// EXPECT_EQ(2000000000, s4->GetInt()); // TODO: uncomment this
s4->SetInt(2000000001);
Field* s5 = statics->GetStaticField(5);
EXPECT_EQ('J', s5->GetType());
-// EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong()); // TODO uncomment this
+// EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong()); // TODO: uncomment this
s5->SetLong(0x34567890abcdef12LL);
Field* s6 = statics->GetStaticField(6);
EXPECT_EQ('F', s6->GetType());
-// EXPECT_EQ(0.5, s6->GetFloat()); // TODO uncomment this
+// EXPECT_EQ(0.5, s6->GetFloat()); // TODO: uncomment this
s6->SetFloat(0.75);
Field* s7 = statics->GetStaticField(7);
EXPECT_EQ('D', s7->GetType());
-// EXPECT_EQ(16777217, s7->GetDouble()); // TODO uncomment this
+// EXPECT_EQ(16777217, s7->GetDouble()); // TODO: uncomment this
s7->SetDouble(16777219);
Field* s8 = statics->GetStaticField(8);
EXPECT_EQ('L', s8->GetType());
-// EXPECT_TRUE(down_cast<String*>(s8->GetObject())->Equals("android")); // TODO uncomment this
+// EXPECT_TRUE(down_cast<String*>(s8->GetObject())->Equals("android")); // TODO: uncomment this
s8->SetObject(String::AllocFromAscii("robot"));
Field* s9 = statics->GetStaticField(9);
EXPECT_EQ('[', s9->GetType());
-// EXPECT_EQ(NULL, s9->GetObject()); // TODO uncomment this
+// EXPECT_EQ(NULL, s9->GetObject()); // TODO: uncomment this
s9->SetObject(NULL);
EXPECT_EQ(false, s0->GetBoolean());
diff --git a/src/common_test.h b/src/common_test.h
index 1fbe718ff8..14a68a2de0 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -1,6 +1,7 @@
// Copyright 2011 Google Inc. All Rights Reserved.
#include <dirent.h>
+#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -11,6 +12,9 @@
#include "class_linker.h"
#include "dex_file.h"
+#include "unicode/uclean.h"
+#include "unicode/uvernum.h"
+
#include "gtest/gtest.h"
namespace art {
@@ -315,11 +319,20 @@ class ScratchFile {
int fd_;
};
-class RuntimeTest : public testing::Test {
+class CommonTest : public testing::Test {
protected:
virtual void SetUp() {
is_host_ = getenv("ANDROID_BUILD_TOP") != NULL;
+ if (is_host_) {
+ // $ANDROID_ROOT is set on the device, but not on the host.
+ // We need to set this so that icu4c can find its locale data.
+ std::string root;
+ root += getenv("ANDROID_BUILD_TOP");
+ root += "/out/host/linux-x86";
+ setenv("ANDROID_ROOT", root.c_str(), 1);
+ }
+
android_data_.reset(strdup(is_host_ ? "/tmp/art-data-XXXXXX" : "/sdcard/art-data-XXXXXX"));
ASSERT_TRUE(android_data_ != NULL);
const char* android_data_modified = mkdtemp(android_data_.get());
@@ -368,6 +381,15 @@ class RuntimeTest : public testing::Test {
ASSERT_EQ(0, rmdir_cache_result);
int rmdir_data_result = rmdir(android_data_.get());
ASSERT_EQ(0, rmdir_data_result);
+
+ // icu4c has a fixed 10-element array "gCommonICUDataArray".
+ // If we run > 10 tests, we fill that array and u_setCommonData fails.
+ // There's a function to clear the array, but it's not public...
+ typedef void (*IcuCleanupFn)();
+ void* sym = dlsym(RTLD_DEFAULT, "u_cleanup_" U_ICU_VERSION_SHORT);
+ CHECK(sym != NULL);
+ IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym);
+ (*icu_cleanup_fn)();
}
std::string GetLibCoreDexFileName() {
@@ -394,7 +416,7 @@ class RuntimeTest : public testing::Test {
bool is_host_;
scoped_ptr_malloc<char> android_data_;
std::string art_cache_;
- scoped_ptr<DexFile> java_lang_dex_file_;
+ scoped_ptr<const DexFile> java_lang_dex_file_;
scoped_ptr<Runtime> runtime_;
ClassLinker* class_linker_;
};
diff --git a/src/dex_cache_test.cc b/src/dex_cache_test.cc
index c439311f25..cb682a08df 100644
--- a/src/dex_cache_test.cc
+++ b/src/dex_cache_test.cc
@@ -12,7 +12,7 @@
namespace art {
-class DexCacheTest : public RuntimeTest {};
+class DexCacheTest : public CommonTest {};
TEST_F(DexCacheTest, Open) {
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 22e4430d46..b9ae7e3b1a 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -35,7 +35,7 @@ DexFile::ClassPathEntry DexFile::FindInClassPath(const StringPiece& descriptor,
return ClassPathEntry(dex_file, dex_class_def);
}
}
- // TODO remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
+ // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
reinterpret_cast<const DexFile::ClassDef*>(NULL));
}
@@ -257,7 +257,7 @@ DexFile* DexFile::OpenZip(const std::string& filename) {
return NULL;
}
- // TODO restat and check length against zip_entry->GetUncompressedLength()?
+ // TODO: restat and check length against zip_entry->GetUncompressedLength()?
// Compute checksum and compare to zip. If things look okay, rename from tmp.
off_t lseek_result = lseek(fd->GetFd(), 0, SEEK_SET);
diff --git a/src/exception_test.cc b/src/exception_test.cc
index ae1546f331..e2f97c0a9c 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -60,7 +60,7 @@ static const char kMyClassExceptionHandleDex[] =
"AQAAABgCAAABEAAAAQAAADgCAAACIAAADQAAAD4CAAADIAAABAAAAN4CAAAEIAAAAQAAAAEDAAAA"
"IAAAAgAAAAkDAAAAEAAAAQAAACgDAAA=";
-class ExceptionTest : public RuntimeTest {
+class ExceptionTest : public CommonTest {
};
TEST_F(ExceptionTest, MyClass_F_G) {
diff --git a/src/file.h b/src/file.h
index 680872e776..3353602145 100644
--- a/src/file.h
+++ b/src/file.h
@@ -33,6 +33,8 @@ class File {
// Returns a negative value if position cannot be determined.
virtual off_t Position() = 0;
+ virtual int Fd() = 0;
+
const char* name() const { return name_; }
protected:
diff --git a/src/file_linux.h b/src/file_linux.h
index 7e36adaf94..3d381d545e 100644
--- a/src/file_linux.h
+++ b/src/file_linux.h
@@ -22,6 +22,10 @@ class LinuxFile : public File {
virtual off_t Length();
virtual off_t Position();
+ virtual int Fd() {
+ return fd_;
+ }
+
private:
static const int kClosedFd = -1;
diff --git a/src/file_test.cc b/src/file_test.cc
index 4afc834d8b..385b3dd647 100644
--- a/src/file_test.cc
+++ b/src/file_test.cc
@@ -9,7 +9,7 @@
namespace art {
-class FileTest : public RuntimeTest {};
+class FileTest : public CommonTest {};
TEST_F(FileTest, Read) {
std::string filename = GetLibCoreDexFileName();
@@ -44,4 +44,13 @@ TEST_F(FileTest, FilePosition) {
EXPECT_EQ(4, file->Position());
}
+
+TEST_F(FileTest, FileFd) {
+ std::string filename = GetLibCoreDexFileName();
+ scoped_ptr<File> file(OS::OpenFile(filename.c_str(), false));
+ ASSERT_TRUE(file != NULL);
+ EXPECT_NE(-1, file->Fd());
+}
+
+
} // namespace art
diff --git a/src/heap.cc b/src/heap.cc
index b9998dae92..de801cebc0 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -14,7 +14,7 @@ namespace art {
std::vector<Space*> Heap::spaces_;
-size_t Heap::startup_size_ = 0;
+Space* Heap::alloc_space_ = NULL;
size_t Heap::maximum_size_ = 0;
@@ -28,14 +28,33 @@ HeapBitmap* Heap::mark_bitmap_ = NULL;
HeapBitmap* Heap::live_bitmap_ = NULL;
-bool Heap::Init(size_t startup_size, size_t maximum_size) {
- Space* space = Space::Create(startup_size, maximum_size);
+bool Heap::Init(size_t initial_size, size_t maximum_size, const char* boot_image_file_name) {
+ Space* boot_space;
+ byte* requested_base;
+ if (boot_image_file_name == NULL) {
+ boot_space = NULL;
+ requested_base = NULL;
+ } else {
+ boot_space = Space::Create(boot_image_file_name);
+ if (boot_space == NULL) {
+ return false;
+ }
+ spaces_.push_back(boot_space);
+ requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize);
+ }
+
+ Space* space = Space::Create(initial_size, maximum_size, requested_base);
if (space == NULL) {
return false;
}
- byte* base = space->GetBase();
- size_t num_bytes = space->Size();
+ if (boot_space == NULL) {
+ boot_space = space;
+ }
+ byte* base = std::min(boot_space->GetBase(), space->GetBase());
+ byte* limit = std::max(boot_space->GetLimit(), space->GetLimit());
+ DCHECK_LT(base, limit);
+ size_t num_bytes = limit - base;
// Allocate the initial live bitmap.
scoped_ptr<HeapBitmap> live_bitmap(HeapBitmap::Create(base, num_bytes));
@@ -49,8 +68,8 @@ bool Heap::Init(size_t startup_size, size_t maximum_size) {
return false;
}
+ alloc_space_ = space;
spaces_.push_back(space);
- startup_size_ = startup_size;
maximum_size_ = maximum_size;
live_bitmap_ = live_bitmap.release();
mark_bitmap_ = mark_bitmap.release();
@@ -62,8 +81,14 @@ bool Heap::Init(size_t startup_size, size_t maximum_size) {
void Heap::Destroy() {
STLDeleteElements(&spaces_);
- delete mark_bitmap_;
- delete live_bitmap_;
+ if (mark_bitmap_ != NULL) {
+ delete mark_bitmap_;
+ mark_bitmap_ = NULL;
+ }
+ if (live_bitmap_ != NULL) {
+ delete live_bitmap_;
+ }
+ live_bitmap_ = NULL;
}
Object* Heap::AllocObject(Class* klass, size_t num_bytes) {
@@ -100,8 +125,8 @@ void Heap::RecordFree(Space* space, const Object* obj) {
}
Object* Heap::Allocate(size_t size) {
- CHECK_EQ(spaces_.size(), 1u);
- Space* space = spaces_[0];
+ DCHECK(alloc_space_ != NULL);
+ Space* space = alloc_space_;
Object* obj = Allocate(space, size);
if (obj != NULL) {
RecordAllocation(space, obj);
diff --git a/src/heap.h b/src/heap.h
index 247c6bad6d..99f655424a 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -24,7 +24,9 @@ class Heap {
typedef void (RootVistor)(Object* root, void* arg);
- static bool Init(size_t starting_size, size_t maximum_size);
+ // Create a heap with the requested sizes. optional boot image may
+ // be NULL, otherwise it is an image filename created by ImageWriter.
+ static bool Init(size_t starting_size, size_t maximum_size, const char* boot_image_file_name);
static void Destroy();
@@ -53,10 +55,6 @@ class Heap {
return mark_bitmap_;
}
- static size_t GetMaximumSize() {
- return maximum_size_;
- }
-
private:
// Allocates uninitialized storage.
static Object* Allocate(size_t num_bytes);
@@ -73,13 +71,13 @@ class Heap {
static std::vector<Space*> spaces_;
+ // default Space for allocations
+ static Space* alloc_space_;
+
static HeapBitmap* mark_bitmap_;
static HeapBitmap* live_bitmap_;
- // The startup size of the heap in bytes.
- static size_t startup_size_;
-
// The maximum size of the heap in bytes.
static size_t maximum_size_;
diff --git a/src/image.cc b/src/image.cc
new file mode 100644
index 0000000000..a41bf5ba14
--- /dev/null
+++ b/src/image.cc
@@ -0,0 +1,10 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "image.h"
+
+namespace art {
+
+const byte ImageHeader::kImageMagic[] = { 'i', 'm', 'g', '\n' };
+const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' };
+
+} // namespace art
diff --git a/src/image.h b/src/image.h
new file mode 100644
index 0000000000..c4123e8c6c
--- /dev/null
+++ b/src/image.h
@@ -0,0 +1,47 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_IMAGE_H_
+#define ART_SRC_IMAGE_H_
+
+#include <string.h>
+
+#include "globals.h"
+
+namespace art {
+
+// header of image files written by ImageWriter, read and validated by Space.
+class ImageHeader {
+ public:
+ ImageHeader() {}
+
+ ImageHeader(uint32_t base_addr) : base_addr_(base_addr) {
+ memcpy(magic_, kImageMagic, sizeof(kImageMagic));
+ memcpy(version_, kImageVersion, sizeof(kImageVersion));
+ }
+
+ bool IsValid() {
+ if (memcmp(magic_, kImageMagic, sizeof(kImageMagic) != 0)) {
+ return false;
+ }
+ if (memcmp(version_, kImageVersion, sizeof(kImageVersion) != 0)) {
+ return false;
+ }
+ return true;
+ }
+
+ byte* GetBaseAddr() const {
+ return reinterpret_cast<byte*>(base_addr_);
+ }
+
+ private:
+ static const byte kImageMagic[4];
+ static const byte kImageVersion[4];
+
+ byte magic_[4];
+ byte version_[4];
+ uint32_t base_addr_;
+};
+
+} // namespace art
+
+#endif // ART_SRC_IMAGE_H_
diff --git a/src/image_test.cc b/src/image_test.cc
index c3662cd403..6cfa62d1d1 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -1,28 +1,42 @@
// Copyright 2011 Google Inc. All Rights Reserved.
#include "common_test.h"
+#include "file.h"
+#include "image.h"
#include "image_writer.h"
+#include "os.h"
+#include "space.h"
#include "gtest/gtest.h"
namespace art {
-class ImageTest : public RuntimeTest {};
+class ImageTest : public CommonTest {};
TEST_F(ImageTest, WriteRead) {
scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
EXPECT_TRUE(libcore_dex_file.get() != NULL);
- // TODO garbage collect before writing
+ // TODO: garbage collect before writing
const std::vector<Space*>& spaces = Heap::GetSpaces();
// can't currently deal with writing a space that might have pointers between spaces
- CHECK_EQ(1U, spaces.size());
+ ASSERT_EQ(1U, spaces.size());
+ Space* space = spaces[0];
ImageWriter writer;
ScratchFile tmp;
const int image_base = 0x5000000;
- bool success = writer.Write(spaces[0], tmp.GetFilename(), reinterpret_cast<byte*>(image_base));
- EXPECT_TRUE(success);
+ bool success = writer.Write(space, tmp.GetFilename(), reinterpret_cast<byte*>(image_base));
+ ASSERT_TRUE(success);
+
+ {
+ scoped_ptr<File> file(OS::OpenFile(tmp.GetFilename(), false));
+ ASSERT_TRUE(file != NULL);
+ ImageHeader image_header;
+ file->ReadFully(&image_header, sizeof(image_header));
+ ASSERT_TRUE(image_header.IsValid());
+ ASSERT_GE(sizeof(image_header) + space->Size(), static_cast<size_t>(file->Length()));
+ }
// tear down old runtime and make a new one
delete runtime_.release();
diff --git a/src/image_writer.cc b/src/image_writer.cc
index beb736662b..b6625d099c 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -8,6 +8,7 @@
#include "file.h"
#include "globals.h"
#include "heap.h"
+#include "image.h"
#include "logging.h"
#include "object.h"
#include "space.h"
@@ -33,11 +34,9 @@ bool ImageWriter::Write(Space* space, const char* filename, byte* image_base) {
bool ImageWriter::Init(Space* space) {
size_t size = space->Size();
int prot = PROT_READ | PROT_WRITE;
- int flags = MAP_PRIVATE | MAP_ANONYMOUS;
size_t length = RoundUp(size, kPageSize);
- image_.reset(MemMap::Map(length, prot, flags));
+ image_.reset(MemMap::Map(length, prot));
if (image_ == NULL) {
- PLOG(ERROR) << "mmap failed";
return false;
}
return true;
@@ -56,8 +55,9 @@ void ImageWriter::CalculateNewObjectOffsets() {
HeapBitmap* heap_bitmap = Heap::GetLiveBits();
DCHECK(heap_bitmap != NULL);
DCHECK_EQ(0U, image_top_);
- // leave a header, ensures objects have non-zero offset for DCHECKs
- image_top_ += 8; // 64-bit-alignment
+ ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_));
+ memcpy(image_->GetAddress(), &image_header, sizeof(image_header));
+ image_top_ += RoundUp(sizeof(image_header), 8); // 64-bit-alignment
heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);
DCHECK_LT(image_top_, image_->GetLength());
// Note that top_ is left at end of used space
@@ -88,7 +88,7 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) {
DCHECK(orig != NULL);
DCHECK(copy != NULL);
copy->klass_ = down_cast<Class*>(GetImageAddress(orig->klass_));
- // TODO specical case init of pointers to malloc data (or removal of these pointers)
+ // TODO: specical case init of pointers to malloc data (or removal of these pointers)
if (orig->IsObjectArray()) {
FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
} else {
diff --git a/src/image_writer.h b/src/image_writer.h
index 0af45c662b..003c4e950f 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -1,7 +1,7 @@
// Copyright 2011 Google Inc. All Rights Reserved.
-#ifndef ART_SRC_IMAGE_H_
-#define ART_SRC_IMAGE_H_
+#ifndef ART_SRC_IMAGE_WRITER_H_
+#define ART_SRC_IMAGE_WRITER_H_
#include <cstddef>
#include <stdint.h>
@@ -66,4 +66,4 @@ class ImageWriter {
} // namespace art
-#endif // ART_SRC_IMAGE_H_
+#endif // ART_SRC_IMAGE_WRITER_H_
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 20f39b6d5d..b30ce539aa 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -269,7 +269,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) {
segmentState.parts.topIndex = topIndex;
} else {
segmentState.parts.topIndex = topIndex-1;
- LOG(INFO) << "+++ ate last entry " << topIndex-1;
+ //LOG(INFO) << "+++ ate last entry " << topIndex-1;
}
} else {
/*
diff --git a/src/indirect_reference_table_test.cc b/src/indirect_reference_table_test.cc
index 97e3a70259..352ebd5c2e 100644
--- a/src/indirect_reference_table_test.cc
+++ b/src/indirect_reference_table_test.cc
@@ -22,7 +22,7 @@
namespace art {
-class IndirectReferenceTableTest : public RuntimeTest {
+class IndirectReferenceTableTest : public CommonTest {
};
TEST_F(IndirectReferenceTableTest, BasicTest) {
diff --git a/src/intern_table_test.cc b/src/intern_table_test.cc
index daae8410fb..e6bb611312 100644
--- a/src/intern_table_test.cc
+++ b/src/intern_table_test.cc
@@ -9,7 +9,7 @@
namespace art {
-class InternTableTest : public RuntimeTest {};
+class InternTableTest : public CommonTest {};
TEST_F(InternTableTest, Intern) {
InternTable intern_table;
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index f3d13c8cc7..440dda54b9 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -382,9 +382,7 @@ void* JniCompiler::AllocateCode(size_t size) {
JniCompiler::JniCompiler() {
// TODO: this shouldn't be managed by the JniCompiler, we should have a
// code cache.
- jni_code_.reset(MemMap::Map(kPageSize,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_ANONYMOUS | MAP_PRIVATE));
+ jni_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC));
CHECK(jni_code_ != NULL);
jni_code_top_ = jni_code_->GetAddress();
}
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 59b9004667..854df887d0 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -15,14 +15,12 @@
namespace art {
-class JniCompilerTest : public RuntimeTest {
+class JniCompilerTest : public CommonTest {
protected:
virtual void SetUp() {
- RuntimeTest::SetUp();
+ CommonTest::SetUp();
// Create thunk code that performs the native to managed transition
- thunk_code_.reset(MemMap::Map(kPageSize,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_ANONYMOUS | MAP_PRIVATE));
+ thunk_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC));
CHECK(thunk_code_ != NULL);
Assembler thk_asm;
// TODO: shouldn't have machine specific code in a general purpose file
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index ef3bf57770..599b0fe68f 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -3,9 +3,10 @@
#include "jni_internal.h"
#include <cstdarg>
-#include <vector>
-#include <utility>
+#include <dlfcn.h>
#include <sys/mman.h>
+#include <utility>
+#include <vector>
#include "class_linker.h"
#include "jni.h"
@@ -18,6 +19,235 @@
namespace art {
+enum JNI_OnLoadState {
+ kPending = 0, /* initial state, must be zero */
+ kFailed,
+ kOkay,
+};
+
+struct SharedLibrary {
+ SharedLibrary() : jni_on_load_lock("JNI_OnLoad") {
+ }
+
+ // Path to library "/system/lib/libjni.so".
+ std::string path;
+
+ // The void* returned by dlopen(3).
+ void* handle;
+
+ // The ClassLoader this library is associated with.
+ Object* class_loader;
+
+ // Guards remaining items.
+ Mutex jni_on_load_lock;
+ // Wait for JNI_OnLoad in other thread.
+ pthread_cond_t jni_on_load_cond;
+ // Recursive invocation guard.
+ uint32_t jni_on_load_tid;
+ // Result of earlier JNI_OnLoad call.
+ JNI_OnLoadState jni_on_load_result;
+};
+
+/*
+ * Check the result of an earlier call to JNI_OnLoad on this library. If
+ * the call has not yet finished in another thread, wait for it.
+ */
+bool CheckOnLoadResult(JavaVMExt* vm, SharedLibrary* library) {
+ Thread* self = Thread::Current();
+ if (library->jni_on_load_tid == self->GetId()) {
+ // Check this so we don't end up waiting for ourselves. We need
+ // to return "true" so the caller can continue.
+ LOG(INFO) << *self << " recursive attempt to load library "
+ << "\"" << library->path << "\"";
+ return true;
+ }
+
+ UNIMPLEMENTED(ERROR) << "need to pthread_cond_wait!";
+ // MutexLock mu(&library->jni_on_load_lock);
+ while (library->jni_on_load_result == kPending) {
+ if (vm->verbose_jni) {
+ LOG(INFO) << "[" << *self << " waiting for \"" << library->path << "\" "
+ << "JNI_OnLoad...]";
+ }
+ Thread::State old_state = self->GetState();
+ self->SetState(Thread::kWaiting); // TODO: VMWAIT
+ // pthread_cond_wait(&library->jni_on_load_cond, &library->jni_on_load_lock);
+ self->SetState(old_state);
+ }
+
+ bool okay = (library->jni_on_load_result == kOkay);
+ if (vm->verbose_jni) {
+ LOG(INFO) << "[Earlier JNI_OnLoad for \"" << library->path << "\" "
+ << (okay ? "succeeded" : "failed") << "]";
+ }
+ return okay;
+}
+
+typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+
+/*
+ * Load native code from the specified absolute pathname. Per the spec,
+ * if we've already loaded a library with the specified pathname, we
+ * return without doing anything.
+ *
+ * TODO? for better results we should absolutify the pathname. For fully
+ * correct results we should stat to get the inode and compare that. The
+ * existing implementation is fine so long as everybody is using
+ * System.loadLibrary.
+ *
+ * The library will be associated with the specified class loader. The JNI
+ * spec says we can't load the same library into more than one class loader.
+ *
+ * Returns "true" on success. On failure, sets *detail to a
+ * human-readable description of the error or NULL if no detail is
+ * available; ownership of the string is transferred to the caller.
+ */
+bool JavaVMExt::LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail) {
+ *detail = NULL;
+
+ // See if we've already loaded this library. If we have, and the class loader
+ // matches, return successfully without doing anything.
+ SharedLibrary* library = libraries[path];
+ if (library != NULL) {
+ if (library->class_loader != class_loader) {
+ LOG(WARNING) << "Shared library \"" << path << "\" already opened by "
+ << "ClassLoader " << library->class_loader << "; "
+ << "can't open in " << class_loader;
+ *detail = strdup("already opened by different ClassLoader");
+ return false;
+ }
+ if (verbose_jni) {
+ LOG(INFO) << "[Shared library \"" << path << "\" already loaded in "
+ << "ClassLoader " << class_loader << "]";
+ }
+ if (!CheckOnLoadResult(this, library)) {
+ *detail = strdup("JNI_OnLoad failed before");
+ return false;
+ }
+ return true;
+ }
+
+ // Open the shared library. Because we're using a full path, the system
+ // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
+ // resolve this library's dependencies though.)
+
+ // Failures here are expected when java.library.path has several entries
+ // and we have to hunt for the lib.
+
+ // The current version of the dynamic linker prints detailed information
+ // about dlopen() failures. Some things to check if the message is
+ // cryptic:
+ // - make sure the library exists on the device
+ // - verify that the right path is being opened (the debug log message
+ // above can help with that)
+ // - check to see if the library is valid (e.g. not zero bytes long)
+ // - check config/prelink-linux-arm.map to ensure that the library
+ // is listed and is not being overrun by the previous entry (if
+ // loading suddenly stops working on a prelinked library, this is
+ // a good one to check)
+ // - write a trivial app that calls sleep() then dlopen(), attach
+ // to it with "strace -p <pid>" while it sleeps, and watch for
+ // attempts to open nonexistent dependent shared libs
+
+ // TODO: automate some of these checks!
+
+ // This can execute slowly for a large library on a busy system, so we
+ // want to switch from RUNNING to VMWAIT while it executes. This allows
+ // the GC to ignore us.
+ Thread* self = Thread::Current();
+ Thread::State old_state = self->GetState();
+ self->SetState(Thread::kWaiting); // TODO: VMWAIT
+ void* handle = dlopen(path.c_str(), RTLD_LAZY);
+ self->SetState(old_state);
+
+ if (verbose_jni) {
+ LOG(INFO) << "[Call to dlopen(\"" << path << "\") returned " << handle << "]";
+ }
+
+ if (handle == NULL) {
+ *detail = strdup(dlerror());
+ return false;
+ }
+
+ // Create a new entry.
+ library = new SharedLibrary;
+ library->path = path;
+ library->handle = handle;
+ library->class_loader = class_loader;
+ UNIMPLEMENTED(ERROR) << "missing pthread_cond_init";
+ // pthread_cond_init(&library->onLoadCond, NULL);
+ library->jni_on_load_tid = self->GetId();
+
+ libraries[path] = library;
+
+// if (pNewEntry != pActualEntry) {
+// LOG(INFO) << "WOW: we lost a race to add a shared library (\"" << path << "\" ClassLoader=" << class_loader <<")";
+// freeSharedLibEntry(pNewEntry);
+// return CheckOnLoadResult(this, pActualEntry);
+// } else
+ {
+ if (verbose_jni) {
+ LOG(INFO) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+ }
+
+ bool result = true;
+ void* sym = dlsym(handle, "JNI_OnLoad");
+ if (sym == NULL) {
+ if (verbose_jni) {
+ LOG(INFO) << "[No JNI_OnLoad found in \"" << path << "\"]";
+ }
+ } else {
+ // Call JNI_OnLoad. We have to override the current class
+ // loader, which will always be "null" since the stuff at the
+ // top of the stack is around Runtime.loadLibrary(). (See
+ // the comments in the JNI FindClass function.)
+ UNIMPLEMENTED(WARNING) << "need to override current class loader";
+ JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
+ //Object* prevOverride = self->classLoaderOverride;
+ //self->classLoaderOverride = classLoader;
+
+ old_state = self->GetState();
+ self->SetState(Thread::kNative);
+ if (verbose_jni) {
+ LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+ }
+ int version = (*jni_on_load)(reinterpret_cast<JavaVM*>(this), NULL);
+ self->SetState(old_state);
+
+ UNIMPLEMENTED(WARNING) << "need to restore current class loader";
+ //self->classLoaderOverride = prevOverride;
+
+ if (version != JNI_VERSION_1_2 &&
+ version != JNI_VERSION_1_4 &&
+ version != JNI_VERSION_1_6) {
+ LOG(WARNING) << "JNI_OnLoad in \"" << path << "\" returned "
+ << "bad version: " << version;
+ // It's unwise to call dlclose() here, but we can mark it
+ // as bad and ensure that future load attempts will fail.
+ // We don't know how far JNI_OnLoad got, so there could
+ // be some partially-initialized stuff accessible through
+ // newly-registered native method calls. We could try to
+ // unregister them, but that doesn't seem worthwhile.
+ result = false;
+ } else {
+ if (verbose_jni) {
+ LOG(INFO) << "[Returned " << (result ? "successfully" : "failure")
+ << " from JNI_OnLoad in \"" << path << "\"]";
+ }
+ }
+ }
+
+ library->jni_on_load_result = result ? kOkay : kFailed;
+ library->jni_on_load_tid = 0;
+
+ // Broadcast a wakeup to anybody sleeping on the condition variable.
+ UNIMPLEMENTED(ERROR) << "missing pthread_cond_broadcast";
+ // MutexLock mu(&library->jni_on_load_lock);
+ // pthread_cond_broadcast(&library->jni_on_load_cond);
+ return result;
+ }
+}
+
// Entry/exit processing for all JNI calls.
//
// This performs the necessary thread state switching, lets us amortize the
@@ -25,7 +255,8 @@ namespace art {
// that are using a JNIEnv on the wrong thread.
class ScopedJniThreadState {
public:
- explicit ScopedJniThreadState(JNIEnv* env) {
+ explicit ScopedJniThreadState(JNIEnv* env)
+ : env_(reinterpret_cast<JNIEnvExt*>(env)) {
self_ = ThreadForEnv(env);
self_->SetState(Thread::kRunnable);
}
@@ -34,6 +265,10 @@ class ScopedJniThreadState {
self_->SetState(Thread::kNative);
}
+ JNIEnvExt* Env() {
+ return env_;
+ }
+
Thread* Self() {
return self_;
}
@@ -52,14 +287,114 @@ class ScopedJniThreadState {
return self;
}
+ JNIEnvExt* env_;
Thread* self_;
DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
};
+/*
+ * Add a local reference for an object to the current stack frame. When
+ * the native function returns, the reference will be discarded.
+ *
+ * We need to allow the same reference to be added multiple times.
+ *
+ * This will be called on otherwise unreferenced objects. We cannot do
+ * GC allocations here, and it's best if we don't grab a mutex.
+ *
+ * Returns the local reference (currently just the same pointer that was
+ * passed in), or NULL on failure.
+ */
template<typename T>
T AddLocalReference(ScopedJniThreadState& ts, Object* obj) {
- UNIMPLEMENTED(WARNING);
- return reinterpret_cast<T>(obj);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ IndirectReferenceTable& locals = ts.Env()->locals;
+
+ uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+ IndirectRef ref = locals.Add(cookie, obj);
+ if (ref == NULL) {
+ // TODO: just change Add's DCHECK to CHECK and lose this?
+ locals.Dump();
+ LOG(FATAL) << "Failed adding to JNI local reference table "
+ << "(has " << locals.Capacity() << " entries)";
+ // TODO: dvmDumpThread(dvmThreadSelf(), false);
+ }
+
+#if 0 // TODO: fix this to understand PushLocalFrame, so we can turn it on.
+ if (ts.Env()->check_jni) {
+ size_t entry_count = locals.Capacity();
+ if (entry_count > 16) {
+ std::string class_name(PrettyDescriptor(obj->GetClass()->GetDescriptor()));
+ LOG(WARNING) << "Warning: more than 16 JNI local references: "
+ << entry_count << " (most recent was a " << class_name << ")";
+ locals.Dump();
+ // TODO: dvmDumpThread(dvmThreadSelf(), false);
+ // dvmAbort();
+ }
+ }
+#endif
+
+ if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+ // Hand out direct pointers to support broken old apps.
+ return reinterpret_cast<T>(obj);
+ }
+
+ return reinterpret_cast<T>(ref);
+}
+
+template<typename T>
+T Decode(ScopedJniThreadState& ts, jobject obj) {
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
+ IndirectRefKind kind = GetIndirectRefKind(ref);
+ Object* result;
+ switch (kind) {
+ case kLocal:
+ {
+ IndirectReferenceTable& locals = ts.Env()->locals;
+ result = locals.Get(ref);
+ break;
+ }
+ case kGlobal:
+ {
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ IndirectReferenceTable& globals = vm->globals;
+ MutexLock mu(&vm->globals_lock);
+ result = globals.Get(ref);
+ break;
+ }
+ case kWeakGlobal:
+ {
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ IndirectReferenceTable& weak_globals = vm->weak_globals;
+ MutexLock mu(&vm->weak_globals_lock);
+ result = weak_globals.Get(ref);
+ if (result == kClearedJniWeakGlobal) {
+ // This is a special case where it's okay to return NULL.
+ return NULL;
+ }
+ break;
+ }
+ case kInvalid:
+ default:
+ if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+ // Assume an invalid local reference is actually a direct pointer.
+ return reinterpret_cast<T>(obj);
+ }
+ LOG(FATAL) << "Invalid indirect reference " << obj;
+ return reinterpret_cast<T>(kInvalidIndirectRefObject);
+ }
+
+ if (result == NULL) {
+ LOG(FATAL) << "JNI ERROR (app bug): use of deleted " << kind << ": "
+ << obj;
+ }
+ return reinterpret_cast<T>(result);
}
void CreateInvokeStub(Assembler* assembler, Method* method);
@@ -85,7 +420,7 @@ bool EnsureInvokeStub(Method* method) {
return true;
}
-byte* CreateArgArray(Method* method, va_list ap) {
+byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, va_list ap) {
size_t num_bytes = method->NumArgArrayBytes();
scoped_array<byte> arg_array(new byte[num_bytes]);
const StringPiece& shorty = method->GetShorty();
@@ -104,8 +439,7 @@ byte* CreateArgArray(Method* method, va_list ap) {
offset += 4;
break;
case 'L': {
- // TODO: DecodeReference
- Object* obj = reinterpret_cast<Object*>(va_arg(ap, jobject));
+ Object* obj = Decode<Object*>(ts, va_arg(ap, jobject));
*reinterpret_cast<Object**>(&arg_array[offset]) = obj;
offset += sizeof(Object*);
break;
@@ -123,7 +457,7 @@ byte* CreateArgArray(Method* method, va_list ap) {
return arg_array.release();
}
-byte* CreateArgArray(Method* method, jvalue* args) {
+byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, jvalue* args) {
size_t num_bytes = method->NumArgArrayBytes();
scoped_array<byte> arg_array(new byte[num_bytes]);
const StringPiece& shorty = method->GetShorty();
@@ -142,8 +476,7 @@ byte* CreateArgArray(Method* method, jvalue* args) {
offset += 4;
break;
case 'L': {
- // TODO: DecodeReference
- Object* obj = reinterpret_cast<Object*>(args[i - 1].l);
+ Object* obj = Decode<Object*>(ts, args[i - 1].l);
*reinterpret_cast<Object**>(&arg_array[offset]) = obj;
offset += sizeof(Object*);
break;
@@ -161,8 +494,8 @@ byte* CreateArgArray(Method* method, jvalue* args) {
return arg_array.release();
}
-JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id,
- byte* args) {
+JValue InvokeWithArgArray(ScopedJniThreadState& ts,
+ Object* obj, jmethodID method_id, byte* args) {
// TODO: DecodeReference
Method* method = reinterpret_cast<Method*>(method_id);
// Call the invoke stub associated with the method
@@ -170,22 +503,22 @@ JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id,
const Method::InvokeStub* stub = method->GetInvokeStub();
CHECK(stub != NULL);
JValue result;
- (*stub)(method, obj, self, args, &result);
+ (*stub)(method, obj, ts.Self(), args, &result);
return result;
}
-JValue InvokeWithJValues(Thread* self, Object* obj, jmethodID method_id,
- jvalue* args) {
+JValue InvokeWithJValues(ScopedJniThreadState& ts,
+ Object* obj, jmethodID method_id, jvalue* args) {
Method* method = reinterpret_cast<Method*>(method_id);
- scoped_array<byte> arg_array(CreateArgArray(method, args));
- return InvokeWithArgArray(self, obj, method_id, arg_array.get());
+ scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
+ return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
}
-JValue InvokeWithVarArgs(Thread* self, Object* obj, jmethodID method_id,
- va_list args) {
+JValue InvokeWithVarArgs(ScopedJniThreadState& ts,
+ Object* obj, jmethodID method_id, va_list args) {
Method* method = reinterpret_cast<Method*>(method_id);
- scoped_array<byte> arg_array(CreateArgArray(method, args));
- return InvokeWithArgArray(self, obj, method_id, arg_array.get());
+ scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
+ return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
}
jint GetVersion(JNIEnv* env) {
@@ -320,14 +653,14 @@ void FatalError(JNIEnv* env, const char* msg) {
jint PushLocalFrame(JNIEnv* env, jint cap) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return 0;
+ UNIMPLEMENTED(WARNING) << "ignoring PushLocalFrame(" << cap << ")";
+ return JNI_OK;
}
jobject PopLocalFrame(JNIEnv* env, jobject res) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ UNIMPLEMENTED(WARNING) << "ignoring PopLocalFrame " << res;
+ return res;
}
jobject NewGlobalRef(JNIEnv* env, jobject lobj) {
@@ -343,7 +676,23 @@ void DeleteGlobalRef(JNIEnv* env, jobject gref) {
void DeleteLocalRef(JNIEnv* env, jobject obj) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(WARNING);
+
+ if (obj == NULL) {
+ return;
+ }
+
+ IndirectReferenceTable& locals = ts.Env()->locals;
+
+ uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+ if (!locals.Remove(cookie, obj)) {
+ // Attempting to delete a local reference that is not in the
+ // topmost local reference frame is a no-op. DeleteLocalRef returns
+ // void and doesn't throw any exceptions, but we should probably
+ // complain about it so the user will notice that things aren't
+ // going quite the way they expect.
+ LOG(WARNING) << "JNI WARNING: DeleteLocalRef(" << obj << ") "
+ << "failed to find entry";
+ }
}
jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) {
@@ -403,10 +752,8 @@ jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
// NB. JNI is different from regular Java instanceof in this respect
return JNI_TRUE;
} else {
- // TODO: retrieve handle value for object
- Object* obj = reinterpret_cast<Object*>(jobj);
- // TODO: retrieve handle value for class
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Object* obj = Decode<Object*>(ts, jobj);
+ Class* klass = Decode<Class*>(ts, clazz);
return Object::InstanceOf(obj, klass) ? JNI_TRUE : JNI_FALSE;
}
}
@@ -414,8 +761,7 @@ jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
jmethodID GetMethodID(JNIEnv* env,
jclass clazz, const char* name, const char* sig) {
ScopedJniThreadState ts(env);
- // TODO: retrieve handle value for class
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Class* klass = Decode<Class*>(ts, clazz);
if (!klass->IsInitialized()) {
// TODO: initialize the class
}
@@ -965,8 +1311,7 @@ void SetDoubleField(JNIEnv* env, jobject obj, jfieldID fieldID, jdouble val) {
jmethodID GetStaticMethodID(JNIEnv* env,
jclass clazz, const char* name, const char* sig) {
ScopedJniThreadState ts(env);
- // TODO: DecodeReference
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Class* klass = Decode<Class*>(ts, clazz);
if (!klass->IsInitialized()) {
// TODO: initialize the class
}
@@ -1005,21 +1350,21 @@ jobject CallStaticObjectMethod(JNIEnv* env,
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
+ JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
return AddLocalReference<jobject>(ts, result.l);
}
jobject CallStaticObjectMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
+ JValue result = InvokeWithVarArgs(ts, NULL, methodID, args);
return AddLocalReference<jobject>(ts, result.l);
}
jobject CallStaticObjectMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- JValue result = InvokeWithJValues(ts.Self(), NULL, methodID, args);
+ JValue result = InvokeWithJValues(ts, NULL, methodID, args);
return AddLocalReference<jobject>(ts, result.l);
}
@@ -1028,171 +1373,171 @@ jboolean CallStaticBooleanMethod(JNIEnv* env,
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).z;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).z;
}
jboolean CallStaticBooleanMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).z;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).z;
}
jboolean CallStaticBooleanMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).z;
+ return InvokeWithJValues(ts, NULL, methodID, args).z;
}
jbyte CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).b;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).b;
}
jbyte CallStaticByteMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).b;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).b;
}
jbyte CallStaticByteMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).b;
+ return InvokeWithJValues(ts, NULL, methodID, args).b;
}
jchar CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).c;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).c;
}
jchar CallStaticCharMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).c;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).c;
}
jchar CallStaticCharMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).c;
+ return InvokeWithJValues(ts, NULL, methodID, args).c;
}
jshort CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).s;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).s;
}
jshort CallStaticShortMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).s;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).s;
}
jshort CallStaticShortMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).s;
+ return InvokeWithJValues(ts, NULL, methodID, args).s;
}
jint CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).i;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).i;
}
jint CallStaticIntMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).i;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).i;
}
jint CallStaticIntMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).i;
+ return InvokeWithJValues(ts, NULL, methodID, args).i;
}
jlong CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).j;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).j;
}
jlong CallStaticLongMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).j;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).j;
}
jlong CallStaticLongMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).j;
+ return InvokeWithJValues(ts, NULL, methodID, args).j;
}
jfloat CallStaticFloatMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).f;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).f;
}
jfloat CallStaticFloatMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).f;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).f;
}
jfloat CallStaticFloatMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).f;
+ return InvokeWithJValues(ts, NULL, methodID, args).f;
}
jdouble CallStaticDoubleMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).d;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).d;
}
jdouble CallStaticDoubleMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).d;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).d;
}
jdouble CallStaticDoubleMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).d;
+ return InvokeWithJValues(ts, NULL, methodID, args).d;
}
void CallStaticVoidMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
+ InvokeWithVarArgs(ts, NULL, methodID, ap);
}
void CallStaticVoidMethodV(JNIEnv* env,
jclass cls, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
+ InvokeWithVarArgs(ts, NULL, methodID, args);
}
void CallStaticVoidMethodA(JNIEnv* env,
jclass cls, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- InvokeWithJValues(ts.Self(), NULL, methodID, args);
+ InvokeWithJValues(ts, NULL, methodID, args);
}
jfieldID GetStaticFieldID(JNIEnv* env,
@@ -1375,9 +1720,8 @@ jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
void SetObjectArrayElement(JNIEnv* env,
jobjectArray java_array, jsize index, jobject java_value) {
ScopedJniThreadState ts(env);
- // TODO: DecodeReference
- ObjectArray<Object>* array = reinterpret_cast<ObjectArray<Object>*>(java_array);
- Object* value = reinterpret_cast<Object*>(java_value);
+ ObjectArray<Object>* array = Decode<ObjectArray<Object>*>(ts, java_array);
+ Object* value = Decode<Object*>(ts, java_value);
array->Set(index, value);
}
@@ -1428,8 +1772,7 @@ jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, jo
CHECK_GE(length, 0); // TODO: ReportJniError
// Compute the array class corresponding to the given element class.
- // TODO: DecodeReference
- Class* element_class = reinterpret_cast<Class*>(element_jclass);
+ Class* element_class = Decode<Class*>(ts, element_jclass);
std::string descriptor;
descriptor += "[";
descriptor += element_class->GetDescriptor().ToString();
@@ -1651,11 +1994,16 @@ void SetDoubleArrayRegion(JNIEnv* env,
jint RegisterNatives(JNIEnv* env,
jclass clazz, const JNINativeMethod* methods, jint nMethods) {
ScopedJniThreadState ts(env);
- // TODO: retrieve handle value for class
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Class* klass = Decode<Class*>(ts, clazz);
for(int i = 0; i < nMethods; i++) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
+
+ if (*sig == '!') {
+ // TODO: fast jni. it's too noisy to log all these.
+ ++sig;
+ }
+
Method* method = klass->FindDirectMethod(name, sig);
if (method == NULL) {
method = klass->FindVirtualMethod(name, sig);
@@ -1704,7 +2052,7 @@ jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
ScopedJniThreadState ts(env);
Runtime* runtime = Runtime::Current();
if (runtime != NULL) {
- *vm = runtime->GetJavaVM();
+ *vm = reinterpret_cast<JavaVM*>(runtime->GetJavaVM());
} else {
*vm = NULL;
}
@@ -2058,7 +2406,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, void** p_env, void* vm_args) {
return JNI_ERR;
} else {
*p_env = reinterpret_cast<JNIEnv*>(Thread::Current()->GetJniEnv());
- *p_vm = runtime->GetJavaVM();
+ *p_vm = reinterpret_cast<JavaVM*>(runtime->GetJavaVM());
return JNI_OK;
}
}
@@ -2069,7 +2417,7 @@ extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
*vm_count = 0;
} else {
*vm_count = 1;
- vms[0] = runtime->GetJavaVM();
+ vms[0] = reinterpret_cast<JavaVM*>(runtime->GetJavaVM());
}
return JNI_OK;
}
@@ -2085,7 +2433,6 @@ jint DestroyJavaVM(JavaVM* vm) {
} else {
JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
delete raw_vm->runtime;
- raw_vm->runtime = NULL;
return JNI_OK;
}
}
@@ -2177,12 +2524,15 @@ static const size_t kGlobalsMax = 51200; // Arbitrary sanity check.
static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check.
-JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni)
+JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni)
: fns(&gInvokeInterface),
runtime(runtime),
check_jni(check_jni),
+ verbose_jni(verbose_jni),
pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
+ globals_lock("JNI global reference table"),
globals(kGlobalsInitial, kGlobalsMax, kGlobal),
+ weak_globals_lock("JNI weak global reference table"),
weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal) {
}
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 50e3925618..74366d3eea 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -9,14 +9,37 @@
#include "indirect_reference_table.h"
#include "macros.h"
#include "reference_table.h"
+#include "thread.h"
+
+#include <map>
+#include <string>
namespace art {
class Runtime;
-class Thread;
+class SharedLibrary;
struct JavaVMExt {
- JavaVMExt(Runtime* runtime, bool check_jni);
+ JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni);
+
+ /*
+ * Load native code from the specified absolute pathname. Per the spec,
+ * if we've already loaded a library with the specified pathname, we
+ * return without doing anything.
+ *
+ * TODO: for better results we should canonicalize the pathname. For fully
+ * correct results we should stat to get the inode and compare that. The
+ * existing implementation is fine so long as everybody is using
+ * System.loadLibrary.
+ *
+ * The library will be associated with the specified class loader. The JNI
+ * spec says we can't load the same library into more than one class loader.
+ *
+ * Returns true on success. On failure, returns false and sets *detail to a
+ * human-readable description of the error or NULL if no detail is
+ * available; ownership of the string is transferred to the caller.
+ */
+ bool LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail);
// Must be first to correspond with JNIEnv.
const struct JNIInvokeInterface* fns;
@@ -24,15 +47,20 @@ struct JavaVMExt {
Runtime* runtime;
bool check_jni;
+ bool verbose_jni;
// Used to hold references to pinned primitive arrays.
ReferenceTable pin_table;
// JNI global references.
+ Mutex globals_lock;
IndirectReferenceTable globals;
// JNI weak global references.
+ Mutex weak_globals_lock;
IndirectReferenceTable weak_globals;
+
+ std::map<std::string, SharedLibrary*> libraries;
};
struct JNIEnvExt {
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 04b3225f4d..4fa60c53da 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -10,10 +10,10 @@
namespace art {
-class JniInternalTest : public RuntimeTest {
+class JniInternalTest : public CommonTest {
protected:
virtual void SetUp() {
- RuntimeTest::SetUp();
+ CommonTest::SetUp();
env_ = Thread::Current()->GetJniEnv();
}
JNIEnv* env_;
diff --git a/src/mark_stack.cc b/src/mark_stack.cc
index 19dc1c1aeb..663cdf72c3 100644
--- a/src/mark_stack.cc
+++ b/src/mark_stack.cc
@@ -10,9 +10,9 @@
namespace art {
-MarkStack* MarkStack::Create(size_t maximum_size) {
+MarkStack* MarkStack::Create() {
scoped_ptr<MarkStack> mark_stack(new MarkStack());
- bool success = mark_stack->Init(maximum_size);
+ bool success = mark_stack->Init();
if (!success) {
return NULL;
} else {
@@ -20,11 +20,10 @@ MarkStack* MarkStack::Create(size_t maximum_size) {
}
}
-bool MarkStack::Init(size_t maximum_size) {
+bool MarkStack::Init() {
size_t length = 64 * MB;
- mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS));
+ mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE));
if (mem_map_ == NULL) {
- PLOG(ERROR) << "mmap failed";
return false;
}
byte* addr = mem_map_->GetAddress();
diff --git a/src/mark_stack.h b/src/mark_stack.h
index 38252c2750..79026cc1fe 100644
--- a/src/mark_stack.h
+++ b/src/mark_stack.h
@@ -14,7 +14,7 @@ class Object;
class MarkStack {
public:
- static MarkStack* Create(size_t maximum_size);
+ static MarkStack* Create();
~MarkStack();
@@ -41,7 +41,7 @@ class MarkStack {
base_(NULL), limit_(NULL), ptr_(NULL) {
}
- bool Init(size_t maximum_size);
+ bool Init();
// Memory mapping of the mark stack.
scoped_ptr<MemMap> mem_map_;
diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc
index 06a1c92055..8541f76c8b 100644
--- a/src/mark_sweep.cc
+++ b/src/mark_sweep.cc
@@ -22,7 +22,7 @@ size_t MarkSweep::reference_pendingNext_offset_ = 0; // TODO
size_t MarkSweep::finalizer_reference_zombie_offset_ = 0; // TODO
bool MarkSweep::Init() {
- mark_stack_ = MarkStack::Create(Heap::GetMaximumSize());
+ mark_stack_ = MarkStack::Create();
if (mark_stack_ == NULL) {
return false;
}
diff --git a/src/mem_map.h b/src/mem_map.h
index 9357b9b7e5..f44939cac7 100644
--- a/src/mem_map.h
+++ b/src/mem_map.h
@@ -30,18 +30,28 @@ class MemMap {
// Request an anonymous region of a specified length.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* Map(size_t length, int prot, int flags) {
+ static MemMap* Map(size_t length, int prot) {
+ return Map(NULL, length, prot);
+ }
+
+ // Request an anonymous region of a specified length and a requested base address.
+ //
+ // On success, returns returns a MemMap instance. On failure, returns a NULL;
+ static MemMap* Map(byte* addr, size_t length, int prot) {
+ CHECK_NE(0U, length);
+ CHECK_NE(0, prot);
size_t page_aligned_size = RoundUp(length, kPageSize);
- byte* addr = reinterpret_cast<byte*>(mmap(NULL,
- page_aligned_size,
- prot,
- MAP_ANONYMOUS | flags,
- -1,
- 0));
- if (addr == MAP_FAILED) {
+ byte* actual = reinterpret_cast<byte*>(mmap(addr,
+ page_aligned_size,
+ prot,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0));
+ if (actual == MAP_FAILED) {
+ PLOG(ERROR) << "mmap failed";
return NULL;
}
- return new MemMap(addr, length, addr, page_aligned_size);
+ return new MemMap(actual, length, actual, page_aligned_size);
}
// Map part of a file, taking care of non-page aligned offsets. The
@@ -49,20 +59,33 @@ class MemMap {
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
static MemMap* Map(size_t length, int prot, int flags, int fd, off_t start) {
+ return Map(NULL, length, prot, flags, fd, start);
+ }
+
+ // Map part of a file, taking care of non-page aligned offsets. The
+ // "start" offset is absolute, not relative. This version allows
+ // requesting a specific address for the base of the mapping.
+ //
+ // On success, returns returns a MemMap instance. On failure, returns a NULL;
+ static MemMap* Map(byte* addr, size_t length, int prot, int flags, int fd, off_t start) {
+ CHECK_NE(0U, length);
+ CHECK_NE(0, prot);
+ CHECK(flags & MAP_SHARED || flags & MAP_PRIVATE);
// adjust to be page-aligned
int page_offset = start % kPageSize;
off_t page_aligned_offset = start - page_offset;
- size_t page_aligned_size = length + page_offset;
- byte* addr = reinterpret_cast<byte*>(mmap(NULL,
- page_aligned_size,
- prot,
- MAP_FILE | flags,
- fd,
- page_aligned_offset));
- if (addr == MAP_FAILED) {
+ size_t page_aligned_size = RoundUp(length + page_offset, kPageSize);
+ byte* actual = reinterpret_cast<byte*>(mmap(addr,
+ page_aligned_size,
+ prot,
+ flags,
+ fd,
+ page_aligned_offset));
+ if (actual == MAP_FAILED) {
+ PLOG(ERROR) << "mmap failed";
return NULL;
}
- return new MemMap(addr+page_offset, length, addr, page_aligned_size);
+ return new MemMap(actual + page_offset, length, actual, page_aligned_size);
}
~MemMap() {
@@ -91,6 +114,10 @@ class MemMap {
return length_;
}
+ byte* GetLimit() const {
+ return addr_ + length_;
+ }
+
private:
MemMap(byte* addr, size_t length, void* base_addr, size_t base_length)
: addr_(addr), length_(length), base_addr_(base_addr), base_length_(base_length) {
diff --git a/src/object.h b/src/object.h
index da4d6f33df..1789ec828e 100644
--- a/src/object.h
+++ b/src/object.h
@@ -712,7 +712,7 @@ class ClassLoader : public Object {
Object* packages_;
ClassLoader* parent_;
- // TODO remove once we can create a real PathClassLoader
+ // TODO: remove once we can create a real PathClassLoader
std::vector<const DexFile*> class_path_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassLoader);
@@ -1293,7 +1293,7 @@ class String : public Object {
int32_t hash_code) {
String* string = Alloc(GetJavaLangString(),
utf16_length);
- // TODO use 16-bit wide memset variant
+ // TODO: use 16-bit wide memset variant
for (int i = 0; i < utf16_length; i++ ) {
string->array_->Set(i, utf16_data_in[i]);
}
@@ -1435,7 +1435,7 @@ class String : public Object {
}
bool Equals(const String* that) const {
- // TODO short circuit on hash_code_
+ // TODO: short circuit on hash_code_
if (this->GetLength() != that->GetLength()) {
return false;
}
diff --git a/src/object_bitmap.cc b/src/object_bitmap.cc
index fc3c2ee4ee..7de498860d 100644
--- a/src/object_bitmap.cc
+++ b/src/object_bitmap.cc
@@ -36,9 +36,8 @@ HeapBitmap* HeapBitmap::Create(byte* base, size_t length) {
bool HeapBitmap::Init(const byte* base, size_t max_size) {
CHECK(base != NULL);
size_t length = HB_OFFSET_TO_INDEX(max_size) * kWordSize;
- mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS));
+ mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE));
if (mem_map_ == NULL) {
- LOG(ERROR) << "mmap failed";
return false;
}
words_ = reinterpret_cast<word*>(mem_map_->GetAddress());
diff --git a/src/object_test.cc b/src/object_test.cc
index e11cf075f3..8f9bed6850 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -13,7 +13,7 @@
namespace art {
-class ObjectTest : public RuntimeTest {
+class ObjectTest : public CommonTest {
protected:
void AssertString(size_t length,
const char* utf8_in,
diff --git a/src/os_linux.cc b/src/os_linux.cc
index 8f5f56a7ea..6d84b51a04 100644
--- a/src/os_linux.cc
+++ b/src/os_linux.cc
@@ -30,7 +30,7 @@ File* OS::FileFromFd(const char* name, int fd) {
bool OS::FileExists(const char* name) {
struct stat st;
if (stat(name, &st) == 0) {
- return S_ISREG(st.st_mode); // TODO Deal with symlinks?
+ return S_ISREG(st.st_mode); // TODO: Deal with symlinks?
} else {
return false;
}
diff --git a/src/reference_table_test.cc b/src/reference_table_test.cc
index c61e3c99c3..4728cb03e4 100644
--- a/src/reference_table_test.cc
+++ b/src/reference_table_test.cc
@@ -8,7 +8,7 @@
namespace art {
-class ReferenceTableTest : public RuntimeTest {
+class ReferenceTableTest : public CommonTest {
};
TEST_F(ReferenceTableTest, Basics) {
diff --git a/src/runtime.cc b/src/runtime.cc
index 2476837830..335396d2ae 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -9,6 +9,7 @@
#include "class_linker.h"
#include "heap.h"
+#include "jni_internal.h"
#include "scoped_ptr.h"
#include "thread.h"
@@ -17,12 +18,13 @@ namespace art {
Runtime* Runtime::instance_ = NULL;
Runtime::~Runtime() {
- // TODO: use a smart pointer instead.
+ // TODO: use smart pointers instead. (we'll need the pimpl idiom.)
delete class_linker_;
Heap::Destroy();
delete thread_list_;
+ delete java_vm_;
// TODO: acquire a static mutex on Runtime to avoid racing.
- CHECK(instance_ == this);
+ CHECK(instance_ == NULL || instance_ == this);
instance_ = NULL;
}
@@ -88,7 +90,6 @@ void ParseClassPath(const char* class_path, std::vector<std::string>& vec) {
// [gG] gigabytes.
//
// "s" should point just past the "-Xm?" part of the string.
-// "min" specifies the lowest acceptable value described by "s".
// "div" specifies a divisor, e.g. 1024 if the value must be a multiple
// of 1024.
//
@@ -111,35 +112,35 @@ size_t ParseMemoryOption(const char *s, size_t div) {
// there should be exactly one more character
// that specifies a multiplier.
if (*s2 != '\0') {
- // The remainder of the string is either a single multiplier
- // character, or nothing to indicate that the value is in
- // bytes.
- char c = *s2++;
- if (*s2 == '\0') {
- size_t mul;
- if (c == '\0') {
- mul = 1;
- } else if (c == 'k' || c == 'K') {
- mul = 1024;
- } else if (c == 'm' || c == 'M') {
- mul = 1024 * 1024;
- } else if (c == 'g' || c == 'G') {
- mul = 1024 * 1024 * 1024;
- } else {
- // Unknown multiplier character.
- return 0;
- }
-
- if (val <= std::numeric_limits<size_t>::max() / mul) {
- val *= mul;
- } else {
- // Clamp to a multiple of 1024.
- val = std::numeric_limits<size_t>::max() & ~(1024-1);
- }
+ // The remainder of the string is either a single multiplier
+ // character, or nothing to indicate that the value is in
+ // bytes.
+ char c = *s2++;
+ if (*s2 == '\0') {
+ size_t mul;
+ if (c == '\0') {
+ mul = 1;
+ } else if (c == 'k' || c == 'K') {
+ mul = KB;
+ } else if (c == 'm' || c == 'M') {
+ mul = MB;
+ } else if (c == 'g' || c == 'G') {
+ mul = GB;
} else {
- // There's more than one character after the numeric part.
+ // Unknown multiplier character.
return 0;
}
+
+ if (val <= std::numeric_limits<size_t>::max() / mul) {
+ val *= mul;
+ } else {
+ // Clamp to a multiple of 1024.
+ val = std::numeric_limits<size_t>::max() & ~(1024-1);
+ }
+ } else {
+ // There's more than one character after the numeric part.
+ return 0;
+ }
}
// The man page says that a -Xm value must be a multiple of 1024.
if (val % div == 0) {
@@ -150,6 +151,16 @@ size_t ParseMemoryOption(const char *s, size_t div) {
return 0;
}
+void LoadJniLibrary(JavaVMExt* vm, const char* name) {
+ // TODO: OS_SHARED_LIB_FORMAT_STR
+ std::string mapped_name(StringPrintf("lib%s.so", name));
+ char* reason = NULL;
+ if (!vm->LoadNativeLibrary(mapped_name, NULL, &reason)) {
+ LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": "
+ << reason;
+ }
+}
+
DexFile* Open(const std::string& filename) {
if (filename.size() < 4) {
LOG(WARNING) << "Ignoring short classpath entry '" << filename << "'";
@@ -181,14 +192,17 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b
const char* boot_class_path = getenv("BOOTCLASSPATH");
parsed->boot_image_ = NULL;
#ifdef NDEBUG
- // CheckJNI is off by default for regular builds...
+ // -Xcheck:jni and -verbose:jni are off by default for regular builds...
parsed->check_jni_ = false;
#else
// ...but on by default in debug builds.
parsed->check_jni_ = true;
+ parsed->verbose_.insert("jni");
#endif
parsed->heap_initial_size_ = Heap::kInitialSize;
parsed->heap_maximum_size_ = Heap::kMaximumSize;
+ parsed->stack_size_ = Thread::kDefaultStackSize;
+
parsed->hook_vfprintf_ = vfprintf;
parsed->hook_exit_ = exit;
parsed->hook_abort_ = abort;
@@ -198,19 +212,62 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b
if (option.starts_with("-Xbootclasspath:")) {
boot_class_path = option.substr(strlen("-Xbootclasspath:")).data();
} else if (option == "bootclasspath") {
- parsed->boot_class_path_ = *reinterpret_cast<const std::vector<DexFile*>*>(options[i].second);
+ const void* dex_vector = options[i].second;
+ const std::vector<DexFile*>* v = reinterpret_cast<const std::vector<DexFile*>*>(dex_vector);
+ if (v == NULL) {
+ if (ignore_unrecognized) {
+ continue;
+ }
+ // TODO: usage
+ LOG(FATAL) << "Could not parse " << option;
+ return NULL;
+ }
+ parsed->boot_class_path_ = *v;
} else if (option.starts_with("-Xbootimage:")) {
parsed->boot_image_ = option.substr(strlen("-Xbootimage:")).data();
} else if (option.starts_with("-Xcheck:jni")) {
parsed->check_jni_ = true;
} else if (option.starts_with("-Xms")) {
- parsed->heap_initial_size_ = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024);
+ size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024);
+ if (size == 0) {
+ if (ignore_unrecognized) {
+ continue;
+ }
+ // TODO: usage
+ LOG(FATAL) << "Could not parse " << option;
+ return NULL;
+ }
+ parsed->heap_initial_size_ = size;
} else if (option.starts_with("-Xmx")) {
- parsed->heap_maximum_size_ = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024);
+ size_t size = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024);
+ if (size == 0) {
+ if (ignore_unrecognized) {
+ continue;
+ }
+ // TODO: usage
+ LOG(FATAL) << "Could not parse " << option;
+ return NULL;
+ }
+ parsed->heap_maximum_size_ = size;
+ } else if (option.starts_with("-Xss")) {
+ size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).data(), 1);
+ if (size == 0) {
+ if (ignore_unrecognized) {
+ continue;
+ }
+ // TODO: usage
+ LOG(FATAL) << "Could not parse " << option;
+ return NULL;
+ }
+ parsed->stack_size_ = size;
} else if (option.starts_with("-D")) {
parsed->properties_.push_back(option.substr(strlen("-D")).data());
} else if (option.starts_with("-verbose:")) {
- Split(option.substr(strlen("-verbose:")).data(), ",", parsed->verbose_);
+ std::vector<std::string> verbose_options;
+ Split(option.substr(strlen("-verbose:")).data(), ",", verbose_options);
+ for (size_t i = 0; i < verbose_options.size(); ++i) {
+ parsed->verbose_.insert(verbose_options[i]);
+ }
} else if (option == "vfprintf") {
parsed->hook_vfprintf_ = reinterpret_cast<int (*)(FILE *, const char*, va_list)>(options[i].second);
} else if (option == "exit") {
@@ -250,34 +307,46 @@ Runtime* Runtime::Create(const Options& options, bool ignore_unrecognized) {
bool success = runtime->Init(options, ignore_unrecognized);
if (!success) {
return NULL;
- } else {
- return Runtime::instance_ = runtime.release();
}
+ instance_ = runtime.release();
+
+ // Most JNI libraries can just use System.loadLibrary, but you can't
+ // if you're the library that implements System.loadLibrary!
+ LoadJniLibrary(instance_->GetJavaVM(), "javacore");
+
+ return instance_;
}
-bool Runtime::Init(const Options& options, bool ignore_unrecognized) {
+bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
CHECK_EQ(kPageSize, sysconf(_SC_PAGE_SIZE));
- scoped_ptr<ParsedOptions> parsed_options(ParsedOptions::Create(options, ignore_unrecognized));
- if (parsed_options == NULL) {
+ scoped_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
+ if (options == NULL) {
return false;
}
- vfprintf_ = parsed_options->hook_vfprintf_;
- exit_ = parsed_options->hook_exit_;
- abort_ = parsed_options->hook_abort_;
+ vfprintf_ = options->hook_vfprintf_;
+ exit_ = options->hook_exit_;
+ abort_ = options->hook_abort_;
+ stack_size_ = options->stack_size_;
thread_list_ = ThreadList::Create();
- Heap::Init(parsed_options->heap_initial_size_,
- parsed_options->heap_maximum_size_);
+ if (!Heap::Init(options->heap_initial_size_,
+ options->heap_maximum_size_,
+ options->boot_image_)) {
+ return false;
+ }
- java_vm_.reset(reinterpret_cast<JavaVM*>(new JavaVMExt(this, parsed_options->check_jni_)));
+ bool verbose_jni = options->verbose_.find("jni") != options->verbose_.end();
+ java_vm_ = new JavaVMExt(this, options->check_jni_, verbose_jni);
- Thread::Init();
+ if (!Thread::Init()) {
+ return false;
+ }
Thread* current_thread = Thread::Attach(this);
thread_list_->Register(current_thread);
- class_linker_ = ClassLinker::Create(parsed_options->boot_class_path_);
+ class_linker_ = ClassLinker::Create(options->boot_class_path_);
return true;
}
diff --git a/src/runtime.h b/src/runtime.h
index 828e677feb..9879023512 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -3,21 +3,26 @@
#ifndef ART_SRC_RUNTIME_H_
#define ART_SRC_RUNTIME_H_
+#include <stdio.h>
+
#include <string>
#include <utility>
#include <vector>
-#include "jni.h"
+#include <jni.h>
+
#include "globals.h"
#include "macros.h"
#include "scoped_ptr.h"
#include "stringpiece.h"
+#include "unordered_set.h"
namespace art {
class ClassLinker;
class DexFile;
class Heap;
+class JavaVMExt;
class String;
class ThreadList;
@@ -36,10 +41,11 @@ class Runtime {
bool check_jni_;
size_t heap_initial_size_;
size_t heap_maximum_size_;
+ size_t stack_size_;
jint (*hook_vfprintf_)(FILE* stream, const char* format, va_list ap);
void (*hook_exit_)(jint status);
void (*hook_abort_)();
- std::vector<std::string> verbose_;
+ std::tr1::unordered_set<std::string> verbose_;
std::vector<std::string> properties_;
private:
@@ -73,27 +79,34 @@ class Runtime {
~Runtime();
- ClassLinker* GetClassLinker() {
+ size_t GetStackSize() const {
+ return stack_size_;
+ }
+
+ ClassLinker* GetClassLinker() const {
return class_linker_;
}
- JavaVM* GetJavaVM() const {
- return java_vm_.get();
+ JavaVMExt* GetJavaVM() const {
+ return java_vm_;
}
private:
static void PlatformAbort(const char*, int);
- Runtime() : thread_list_(NULL), class_linker_(NULL) {}
+ Runtime() : stack_size_(0), thread_list_(NULL), class_linker_(NULL) {}
// Initializes a new uninitialized runtime.
bool Init(const Options& options, bool ignore_unrecognized);
+ // The default stack size for managed threads created by the runtime.
+ size_t stack_size_;
+
ThreadList* thread_list_;
ClassLinker* class_linker_;
- scoped_ptr<JavaVM> java_vm_;
+ JavaVMExt* java_vm_;
// Hooks supported by JNI_CreateJavaVM
jint (*vfprintf_)(FILE* stream, const char* format, va_list ap);
diff --git a/src/runtime_test.cc b/src/runtime_test.cc
index 3aa257d74c..6fc14b8872 100644
--- a/src/runtime_test.cc
+++ b/src/runtime_test.cc
@@ -1,16 +1,14 @@
// Copyright 2011 Google Inc. All Rights Reserved.
#include "runtime.h"
-
-#include "gtest/gtest.h"
+#include "common_test.h"
namespace art {
void ParseClassPath(const char* class_path, std::vector<std::string>& vec);
-}
-namespace {
+class RuntimeTest : public CommonTest {};
-TEST(RuntimeTest, ParseClassPath) {
+TEST_F(RuntimeTest, ParseClassPath) {
std::vector<std::string> vec;
art::ParseClassPath("", vec);
@@ -66,4 +64,48 @@ TEST(RuntimeTest, ParseClassPath) {
vec.clear();
}
-} // namespace
+TEST_F(RuntimeTest, ParsedOptions) {
+ void* test_vfprintf = reinterpret_cast<void*>(0xa);
+ void* test_abort = reinterpret_cast<void*>(0xb);
+ void* test_exit = reinterpret_cast<void*>(0xc);
+ void* null = reinterpret_cast<void*>(NULL);
+ scoped_ptr<const DexFile> java_lang_dex_file(GetLibCoreDex());
+ std::vector<const DexFile*> boot_class_path;
+ boot_class_path.push_back(java_lang_dex_file_.get());
+
+ Runtime::Options options;
+ options.push_back(std::make_pair("-Xbootclasspath:class_path_foo:class_path_bar", null));
+ options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+ options.push_back(std::make_pair("-Xbootimage:boot_image", null));
+ options.push_back(std::make_pair("-Xcheck:jni", null));
+ options.push_back(std::make_pair("-Xms2048", null));
+ options.push_back(std::make_pair("-Xmx4k", null));
+ options.push_back(std::make_pair("-Xss1m", null));
+ options.push_back(std::make_pair("-Dfoo=bar", null));
+ options.push_back(std::make_pair("-Dbaz=qux", null));
+ options.push_back(std::make_pair("-verbose:gc,class,jni", null));
+ options.push_back(std::make_pair("vfprintf", test_vfprintf));
+ options.push_back(std::make_pair("abort", test_abort));
+ options.push_back(std::make_pair("exit", test_exit));
+ scoped_ptr<Runtime::ParsedOptions> parsed(Runtime::ParsedOptions::Create(options, false));
+ ASSERT_TRUE(parsed != NULL);
+
+ EXPECT_EQ(1U, parsed->boot_class_path_.size()); // bootclasspath overrides -Xbootclasspath
+ EXPECT_STREQ("boot_image", parsed->boot_image_);
+ EXPECT_EQ(true, parsed->check_jni_);
+ EXPECT_EQ(2048U, parsed->heap_initial_size_);
+ EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
+ EXPECT_EQ(1 * MB, parsed->stack_size_);
+ EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_);
+ EXPECT_TRUE(test_exit == parsed->hook_exit_);
+ EXPECT_TRUE(test_abort == parsed->hook_abort_);
+ ASSERT_EQ(3U, parsed->verbose_.size());
+ EXPECT_TRUE(parsed->verbose_.find("gc") != parsed->verbose_.end());
+ EXPECT_TRUE(parsed->verbose_.find("class") != parsed->verbose_.end());
+ EXPECT_TRUE(parsed->verbose_.find("jni") != parsed->verbose_.end());
+ ASSERT_EQ(2U, parsed->properties_.size());
+ EXPECT_EQ("foo=bar", parsed->properties_[0]);
+ EXPECT_EQ("baz=qux", parsed->properties_[1]);
+}
+
+} // namespace art
diff --git a/src/space.cc b/src/space.cc
index c3d763c4fe..8048f29198 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -4,16 +4,19 @@
#include <sys/mman.h>
+#include "file.h"
+#include "image.h"
#include "logging.h"
#include "mspace.h"
+#include "os.h"
#include "scoped_ptr.h"
#include "utils.h"
namespace art {
-Space* Space::Create(size_t startup_size, size_t maximum_size) {
- scoped_ptr<Space> space(new Space(startup_size, maximum_size));
- bool success = space->Init();
+Space* Space::Create(size_t initial_size, size_t maximum_size, byte* requested_base) {
+ scoped_ptr<Space> space(new Space());
+ bool success = space->Init(initial_size, maximum_size, requested_base);
if (!success) {
return NULL;
} else {
@@ -21,18 +24,31 @@ Space* Space::Create(size_t startup_size, size_t maximum_size) {
}
}
+Space* Space::Create(const char* image_file_name) {
+ CHECK(image_file_name != NULL);
+ scoped_ptr<Space> space(new Space());
+ bool success = space->Init(image_file_name);
+ if (!success) {
+ return NULL;
+ } else {
+ return space.release();
+ }
+}
+
+Space::~Space() {}
+
void* Space::CreateMallocSpace(void* base,
- size_t startup_size,
+ size_t initial_size,
size_t maximum_size) {
errno = 0;
bool is_locked = false;
- size_t commit_size = startup_size / 2;
+ size_t commit_size = initial_size / 2;
void* msp = create_contiguous_mspace_with_base(commit_size, maximum_size,
is_locked, base);
if (msp != NULL) {
// Do not permit the heap grow past the starting size without our
// intervention.
- mspace_set_max_allowed_footprint(msp, startup_size);
+ mspace_set_max_allowed_footprint(msp, initial_size);
} else {
// There is no guarantee that errno has meaning when the call
// fails, but it often does.
@@ -41,35 +57,60 @@ void* Space::CreateMallocSpace(void* base,
return msp;
}
-bool Space::Init() {
- if (!(startup_size_ <= maximum_size_)) {
+bool Space::Init(size_t initial_size, size_t maximum_size, byte* requested_base) {
+ if (!(initial_size <= maximum_size)) {
return false;
}
- size_t length = RoundUp(maximum_size_, kPageSize);
+ size_t length = RoundUp(maximum_size, kPageSize);
int prot = PROT_READ | PROT_WRITE;
- int flags = MAP_PRIVATE | MAP_ANONYMOUS;
- mem_map_.reset(MemMap::Map(length, prot, flags));
- if (mem_map_ == NULL) {
- PLOG(ERROR) << "mmap failed";
+ scoped_ptr<MemMap> mem_map(MemMap::Map(requested_base, length, prot));
+ if (mem_map == NULL) {
return false;
}
+ Init(mem_map.release());
+ maximum_size_ = maximum_size;
+ mspace_ = CreateMallocSpace(base_, initial_size, maximum_size);
+ return (mspace_ != NULL);
+}
+
+void Space::Init(MemMap* mem_map) {
+ mem_map_.reset(mem_map);
base_ = mem_map_->GetAddress();
- limit_ = base_ + length;
- mspace_ = CreateMallocSpace(base_, startup_size_, maximum_size_);
- if (mspace_ == NULL) {
- mem_map_->Unmap();
+ limit_ = base_ + mem_map->GetLength();
+}
+
+
+bool Space::Init(const char* image_file_name) {
+ scoped_ptr<File> file(OS::OpenFile(image_file_name, false));
+ if (file == NULL) {
+ return false;
+ }
+ ImageHeader image_header;
+ bool success = file->ReadFully(&image_header, sizeof(image_header));
+ if (!success || !image_header.IsValid()) {
+ return false;
+ }
+ scoped_ptr<MemMap> map(MemMap::Map(image_header.GetBaseAddr(),
+ file->Length(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED,
+ file->Fd(),
+ 0));
+ if (map == NULL) {
return false;
}
+ CHECK_EQ(image_header.GetBaseAddr(), map->GetAddress());
+ Init(map.release());
return true;
}
-Space::~Space() {}
-
Object* Space::AllocWithoutGrowth(size_t num_bytes) {
+ DCHECK(mspace_ != NULL);
return reinterpret_cast<Object*>(mspace_calloc(mspace_, 1, num_bytes));
}
Object* Space::AllocWithGrowth(size_t num_bytes) {
+ DCHECK(mspace_ != NULL);
// Grow as much as possible within the mspace.
size_t max_allowed = maximum_size_;
mspace_set_max_allowed_footprint(mspace_, max_allowed);
@@ -83,6 +124,7 @@ Object* Space::AllocWithGrowth(size_t num_bytes) {
}
size_t Space::Free(void* ptr) {
+ DCHECK(mspace_ != NULL);
DCHECK(ptr != NULL);
size_t num_bytes = mspace_usable_size(mspace_, ptr);
mspace_free(mspace_, ptr);
@@ -90,6 +132,7 @@ size_t Space::Free(void* ptr) {
}
size_t Space::AllocationSize(const Object* obj) {
+ DCHECK(mspace_ != NULL);
return mspace_usable_size(mspace_, obj) + kChunkOverhead;
}
@@ -116,6 +159,7 @@ void Space::Trim() {
}
size_t Space::MaxAllowedFootprint() {
+ DCHECK(mspace_ != NULL);
return mspace_max_allowed_footprint(mspace_);
}
diff --git a/src/space.h b/src/space.h
index 7b72bba53c..57f676def3 100644
--- a/src/space.h
+++ b/src/space.h
@@ -15,7 +15,11 @@ class Object;
// A space contains memory allocated for managed objects.
class Space {
public:
- static Space* Create(size_t startup_size, size_t maximum_size);
+ // create a Space with the requested sizes requesting a specific base address.
+ static Space* Create(size_t initial_size, size_t maximum_size, byte* requested_base);
+
+ // create a Space from an image file. cannot be used for future allocation or collected.
+ static Space* Create(const char* image);
~Space();
@@ -53,22 +57,27 @@ class Space {
// The boundary tag overhead.
static const size_t kChunkOverhead = kWordSize;
- Space(size_t startup_size, size_t maximum_size) :
- mspace_(NULL),
- base_(NULL),
- startup_size_(startup_size),
- maximum_size_(maximum_size) {
- }
+ // create a Space from an existing memory mapping, taking ownership of the address space.
+ static Space* Create(MemMap* mem_map);
+
+ Space() : mspace_(NULL), maximum_size_(0), base_(0), limit_(0) {}
// Initializes the space and underlying storage.
- bool Init();
+ bool Init(size_t initial_size, size_t maximum_size, byte* requested_base);
+
+ // Initializes the space from existing storage, taking ownership of the storage.
+ void Init(MemMap* map);
+
+ // Initializes the space from an image file
+ bool Init(const char* image_file_name);
- void* CreateMallocSpace(void* base, size_t startup_size,
- size_t maximum_size);
+ void* CreateMallocSpace(void* base, size_t initial_size, size_t maximum_size);
static void DontNeed(void* start, void* end, void* num_bytes);
+ // TODO: have a Space subclass for methods that depend on mspace_ and maximum_size_
void* mspace_;
+ size_t maximum_size_;
scoped_ptr<MemMap> mem_map_;
@@ -76,13 +85,9 @@ class Space {
byte* limit_;
- size_t startup_size_;
-
- size_t maximum_size_;
-
- bool is_condemned_;
+ // bool is_condemned_; // TODO: with IsCondemned
- DISALLOW_IMPLICIT_CONSTRUCTORS(Space);
+ DISALLOW_COPY_AND_ASSIGN(Space);
};
} // namespace art
diff --git a/src/space_test.cc b/src/space_test.cc
index f3c713bb99..73840214b1 100644
--- a/src/space_test.cc
+++ b/src/space_test.cc
@@ -12,23 +12,23 @@ namespace art {
TEST(SpaceTest, Init) {
{
// Less than
- scoped_ptr<Space> space(Space::Create(16 * MB, 32 * MB));
+ scoped_ptr<Space> space(Space::Create(16 * MB, 32 * MB, NULL));
EXPECT_TRUE(space != NULL);
}
{
// Equal to
- scoped_ptr<Space> space(Space::Create(16 * MB, 16 * MB));
+ scoped_ptr<Space> space(Space::Create(16 * MB, 16 * MB, NULL));
EXPECT_TRUE(space != NULL);
}
{
// Greater than
- scoped_ptr<Space> space(Space::Create(32 * MB, 16 * MB));
+ scoped_ptr<Space> space(Space::Create(32 * MB, 16 * MB, NULL));
EXPECT_TRUE(space == NULL);
}
}
TEST(SpaceTest, AllocAndFree) {
- scoped_ptr<Space> space(Space::Create(4 * MB, 16 * MB));
+ scoped_ptr<Space> space(Space::Create(4 * MB, 16 * MB, NULL));
ASSERT_TRUE(space != NULL);
// Succeeds, fits without adjusting the max allowed footprint.
diff --git a/src/thread.cc b/src/thread.cc
index baa659c4d7..eb7a4f727d 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -9,6 +9,7 @@
#include <list>
#include "class_linker.h"
+#include "jni_internal.h"
#include "object.h"
#include "runtime.h"
#include "utils.h"
@@ -53,20 +54,21 @@ void* ThreadStart(void *arg) {
return NULL;
}
-Thread* Thread::Create(size_t stack_size) {
- int prot = PROT_READ | PROT_WRITE;
- // TODO: require the stack size to be page aligned?
- size_t length = RoundUp(stack_size, 0x1000);
- void* stack_limit = mmap(NULL, length, prot, MAP_PRIVATE, -1, 0);
- if (stack_limit == MAP_FAILED) {
- LOG(FATAL) << "mmap";
- return false;
+Thread* Thread::Create(const Runtime* runtime) {
+ size_t stack_size = runtime->GetStackSize();
+ scoped_ptr<MemMap> stack(MemMap::Map(stack_size, PROT_READ | PROT_WRITE));
+ if (stack == NULL) {
+ LOG(FATAL) << "failed to allocate thread stack";
+ // notreached
+ return NULL;
}
Thread* new_thread = new Thread;
new_thread->InitCpu();
- new_thread->stack_limit_ = static_cast<byte*>(stack_limit);
- new_thread->stack_base_ = new_thread->stack_limit_ + length;
+ new_thread->stack_.reset(stack.release());
+ // Since stacks are assumed to grown downward the base is the limit and the limit is the base.
+ new_thread->stack_limit_ = stack->GetAddress();
+ new_thread->stack_base_ = stack->GetLimit();
pthread_attr_t attr;
int result = pthread_attr_init(&attr);
@@ -103,7 +105,7 @@ Thread* Thread::Attach(const Runtime* runtime) {
PLOG(FATAL) << "pthread_setspecific failed";
}
- JavaVMExt* vm = reinterpret_cast<JavaVMExt*>(runtime->GetJavaVM());
+ JavaVMExt* vm = runtime->GetJavaVM();
CHECK(vm != NULL);
bool check_jni = vm->check_jni;
thread->jni_env_ = reinterpret_cast<JNIEnv*>(new JNIEnvExt(thread, check_jni));
@@ -211,9 +213,9 @@ ThreadList::~ThreadList() {
// Make sure that all threads have exited and unregistered when we
// reach this point. This means that all daemon threads had been
// shutdown cleanly.
- CHECK_EQ(list_.size(), 1U);
+ CHECK_LE(list_.size(), 1U);
// TODO: wait for all other threads to unregister
- CHECK_EQ(list_.front(), Thread::Current());
+ CHECK(list_.size() == 0 || list_.front() == Thread::Current());
// TODO: detach the current thread
delete lock_;
lock_ = NULL;
diff --git a/src/thread.h b/src/thread.h
index de5893e848..71fbd48cb9 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -7,9 +7,9 @@
#include <list>
#include "globals.h"
-#include "jni_internal.h"
#include "logging.h"
#include "macros.h"
+#include "mem_map.h"
#include "offsets.h"
#include "runtime.h"
@@ -115,7 +115,7 @@ class Thread {
static const size_t kDefaultStackSize = 64 * KB;
// Creates a new thread.
- static Thread* Create(size_t stack_size);
+ static Thread* Create(const Runtime* runtime);
// Creates a new thread from the calling thread.
static Thread* Attach(const Runtime* runtime);
@@ -292,6 +292,9 @@ class Thread {
// at the next poll.
int suspend_count_;
+ // The memory mapping of the stack for non-attached threads.
+ scoped_ptr<MemMap> stack_;
+
// The inclusive base of the control stack.
byte* stack_base_;
diff --git a/src/unordered_map.h b/src/unordered_map.h
index 66613c6a01..44e5417da6 100644
--- a/src/unordered_map.h
+++ b/src/unordered_map.h
@@ -11,6 +11,7 @@
#include <tr1/unordered_map>
#endif
+//TODO: move out to stringpiece.h?
namespace std {
#ifndef __ANDROID__
namespace tr1 {
diff --git a/src/unordered_set.h b/src/unordered_set.h
new file mode 100644
index 0000000000..18ebaac38a
--- /dev/null
+++ b/src/unordered_set.h
@@ -0,0 +1,12 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_CLASS_UNORDERED_SET_H_
+#define ART_SRC_CLASS_UNORDERED_SET_H_
+
+#ifdef __ANDROID__
+#include <unordered_set>
+#else
+#include <tr1/unordered_set>
+#endif
+
+#endif // ART_SRC_CLASS_UNORDERED_SET_H_
diff --git a/src/utils_test.cc b/src/utils_test.cc
index 1b7315f395..dd411c20c5 100644
--- a/src/utils_test.cc
+++ b/src/utils_test.cc
@@ -8,7 +8,7 @@
namespace art {
-class UtilsTest : public RuntimeTest {
+class UtilsTest : public CommonTest {
};
TEST(PrettyDescriptorTest, ArrayReferences) {
diff --git a/src/zip_archive.cc b/src/zip_archive.cc
index fe263baec5..cccaf82d60 100644
--- a/src/zip_archive.cc
+++ b/src/zip_archive.cc
@@ -386,7 +386,6 @@ bool ZipArchive::MapCentralDirectory() {
// It all looks good. Create a mapping for the CD.
dir_map_.reset(MemMap::Map(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset));
if (dir_map_ == NULL) {
- LOG(WARNING) << "Zip: cd map failed " << strerror(errno);
return false;
}
diff --git a/src/zip_archive_test.cc b/src/zip_archive_test.cc
index 74b3795d82..19f4ee0a33 100644
--- a/src/zip_archive_test.cc
+++ b/src/zip_archive_test.cc
@@ -11,7 +11,7 @@
namespace art {
-class ZipArchiveTest : public RuntimeTest {};
+class ZipArchiveTest : public CommonTest {};
TEST_F(ZipArchiveTest, FindAndExtract) {
scoped_ptr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName()));