First pass of ImageWriter

Change-Id: I4f189587a2e3cc1c265200b8fa64321b299947eb
diff --git a/build/Android.common.mk b/build/Android.common.mk
index c07e6bd..08dc43f 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -40,7 +40,10 @@
 	src/dex_file.cc \
 	src/dex_instruction.cc \
 	src/dex_verifier.cc \
+	src/file.cc \
+	src/file_linux.cc \
 	src/heap.cc \
+	src/image_writer.cc \
 	src/intern_table.cc \
 	src/jni_compiler.cc \
 	src/jni_internal.cc \
@@ -51,6 +54,7 @@
 	src/object.cc \
 	src/object_bitmap.cc \
 	src/offsets.cc \
+	src/os_linux.cc \
 	src/runtime.cc \
 	src/space.cc \
 	src/stringpiece.cc \
@@ -87,6 +91,8 @@
 	src/dex_file_test.cc \
 	src/dex_instruction_visitor_test.cc \
 	src/exception_test.cc \
+	src/file_test.cc \
+	src/image_test.cc \
 	src/intern_table_test.cc \
 	src/jni_internal_test.cc.arm \
 	src/jni_compiler_test.cc.arm \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 64b31a2..50b1a07 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -43,10 +43,14 @@
   java_lang_Object->descriptor_ = "Ljava/lang/Object;";
   // backfill Object as the super class of Class
   java_lang_Class->super_class_ = java_lang_Object;
+  // mark as non-primitive for object_array_class
+  java_lang_Object->primitive_type_ = Class::kPrimNot;
 
   // object_array_class is for root_classes to provide the storage for these classes
   Class* object_array_class = AllocClass(java_lang_Class);
   CHECK(object_array_class != NULL);
+  object_array_class->descriptor_ = "[Ljava/lang/Object;";
+  object_array_class->component_type_ = java_lang_Object;
 
   // String and char[] are necessary so that FindClass can assign names to members
   Class* java_lang_String = AllocClass(java_lang_Class);
@@ -56,12 +60,15 @@
   java_lang_String->object_size_ = sizeof(String);
   Class* char_array_class = AllocClass(java_lang_Class);
   CHECK(char_array_class != NULL);
+  char_array_class->descriptor_ = "[C";
 
   // int[] and long[] are used for static field storage
   Class* int_array_class = AllocClass(java_lang_Class);
   CHECK(int_array_class != NULL);
+  int_array_class->descriptor_ = "[I";
   Class* long_array_class = AllocClass(java_lang_Class);
   CHECK(long_array_class != NULL);
+  long_array_class->descriptor_ = "[J";
 
   // Field and Method are necessary so that FindClass can link members
   Class* java_lang_reflect_Field = AllocClass(java_lang_Class);
@@ -219,7 +226,7 @@
 }
 
 Class* ClassLinker::AllocClass(Class* java_lang_Class) {
-  return down_cast<Class*>(java_lang_Class->NewInstance());
+  return java_lang_Class->NewInstance()->AsClass();
 }
 
 Class* ClassLinker::AllocClass() {
diff --git a/src/common_test.h b/src/common_test.h
index b9e7bcc..9acb06c 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -395,6 +395,38 @@
   return dex_file;
 }
 
+class ScratchFile {
+ public:
+  ScratchFile() {
+    std::string filename_template;
+    filename_template = getenv("ANDROID_DATA");
+    filename_template += "/TmpFile-XXXXXX";
+    filename_.reset(strdup(filename_template.c_str()));
+    CHECK(filename_ != NULL);
+    fd_ = mkstemp(filename_.get());
+    CHECK_NE(-1, fd_);
+  }
+
+  ~ScratchFile() {
+    int unlink_result = unlink(filename_.get());
+    CHECK_EQ(0, unlink_result);
+    int close_result = close(fd_);
+    CHECK_EQ(0, close_result);
+  }
+
+  const char* GetFilename() const {
+    return filename_.get();
+  }
+
+  int GetFd() const {
+    return fd_;
+  }
+
+ private:
+  scoped_ptr_malloc<char> filename_;
+  int fd_;
+};
+
 class RuntimeTest : public testing::Test {
  protected:
   virtual void SetUp() {
diff --git a/src/dex_file.cc b/src/dex_file.cc
index da6ae9d..0ccdba4 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -14,6 +14,7 @@
 #include "globals.h"
 #include "logging.h"
 #include "object.h"
+#include "os.h"
 #include "scoped_ptr.h"
 #include "stringprintf.h"
 #include "thread.h"
@@ -151,15 +152,15 @@
                                 adjacent_dex_filename.end(),
                                 ".dex");
   // Example adjacent_dex_filename = dir/foo.dex
-  struct stat sb;
-  if (stat(adjacent_dex_filename.c_str(), &sb) == 0) {
+  if (OS::FileExists(adjacent_dex_filename.c_str())) {
     DexFile* adjacent_dex_file = DexFile::OpenFile(adjacent_dex_filename);
     if (adjacent_dex_file != NULL) {
-      // We don't verify anything in this case, because we aren't in
-      // the cache and typically the file is in the readonly /system
-      // area, so if something is wrong, there is nothing we can do.
-      return adjacent_dex_file;
+        // We don't verify anything in this case, because we aren't in
+        // the cache and typically the file is in the readonly /system
+        // area, so if something is wrong, there is nothing we can do.
+        return adjacent_dex_file;
     }
+    return NULL;
   }
 
   char resolved[PATH_MAX];
@@ -198,9 +199,11 @@
   // Example cache_path = /data/art-cache/parent@dir@foo.jar@classes.dex.1a2b3c4d
 
   while (true) {
-    DexFile* cached_dex_file = DexFile::OpenFile(cache_path);
-    if (cached_dex_file != NULL) {
-      return cached_dex_file;
+    if (OS::FileExists(cache_path.c_str())) {
+      DexFile* cached_dex_file = DexFile::OpenFile(cache_path);
+      if (cached_dex_file != NULL) {
+        return cached_dex_file;
+      }
     }
 
     // Try to open the temporary cache file, grabbing an exclusive
@@ -245,7 +248,11 @@
 
     // We have the correct file open and locked. Extract classes.dex
     TmpFile tmp_file(cache_path_tmp);
-    bool success = zip_entry->Extract(fd->GetFd());
+    scoped_ptr<File> file(OS::FileFromFd(cache_path_tmp.c_str(), fd->GetFd()));
+    if (file == NULL) {
+      return NULL;
+    }
+    bool success = zip_entry->Extract(*file);
     if (!success) {
       return NULL;
     }
diff --git a/src/file.cc b/src/file.cc
new file mode 100644
index 0000000..df934e9
--- /dev/null
+++ b/src/file.cc
@@ -0,0 +1,36 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+#include "file.h"
+
+namespace art {
+
+bool File::ReadFully(void* buffer, int64_t num_bytes) {
+  int64_t remaining = num_bytes;
+  char* current_buffer = reinterpret_cast<char*>(buffer);
+  while (remaining > 0) {
+    int bytes_read = Read(current_buffer, remaining);
+    if (bytes_read <= 0) {
+      return false;
+    }
+    remaining -= bytes_read;  // Reduce the number of remaining bytes.
+    current_buffer += bytes_read;  // Move the buffer forward.
+  }
+  return true;
+}
+
+
+bool File::WriteFully(const void* buffer, int64_t num_bytes) {
+  int64_t remaining = num_bytes;
+  const char* current_buffer = reinterpret_cast<const char*>(buffer);
+  while (remaining > 0) {
+    int bytes_read = Write(current_buffer, remaining);
+    if (bytes_read < 0) {
+      return false;
+    }
+    remaining -= bytes_read;  // Reduce the number of remaining bytes.
+    current_buffer += bytes_read;  // Move the buffer forward.
+  }
+  return true;
+}
+
+}  // namespace art
diff --git a/src/file.h b/src/file.h
new file mode 100644
index 0000000..680872e
--- /dev/null
+++ b/src/file.h
@@ -0,0 +1,49 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_FILE_H_
+#define ART_SRC_FILE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace art {
+
+class File {
+ public:
+  virtual ~File() { }
+
+  virtual int64_t Read(void* buffer, int64_t num_bytes) = 0;
+  virtual int64_t Write(const void* buffer, int64_t num_bytes) = 0;
+
+  // ReadFully and WriteFully do attempt to transfer all of the bytes to/from
+  // the buffer. In the event of short accesses they will loop internally until
+  // the whole buffer has been transferred or an error occurs. If an error
+  // occurred the result will be set to false.
+  virtual bool ReadFully(void* buffer, int64_t num_bytes);
+  virtual bool WriteFully(const void* buffer, int64_t num_bytes);
+  bool WriteByte(uint8_t byte) {
+    return WriteFully(&byte, 1);
+  }
+
+  // Get the length of the file. Returns a negative value if the length cannot
+  // be determined (e.g. not seekable device).
+  virtual off_t Length() = 0;
+
+  // Get the current position in the file.
+  // Returns a negative value if position cannot be determined.
+  virtual off_t Position() = 0;
+
+  const char* name() const { return name_; }
+
+ protected:
+  explicit File(const char* name) : name_(name) { }
+  virtual void Close() = 0;
+  virtual bool IsClosed() = 0;
+
+ private:
+  const char* name_;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_FILE_H_
diff --git a/src/file_linux.cc b/src/file_linux.cc
new file mode 100644
index 0000000..367dbe8
--- /dev/null
+++ b/src/file_linux.cc
@@ -0,0 +1,64 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+#include "file_linux.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "logging.h"
+
+namespace art {
+
+LinuxFile::~LinuxFile() {
+  // Close the file if necessary (unless it's a standard stream).
+  if (auto_close_ && fd_ > STDERR_FILENO) {
+    Close();
+  }
+}
+
+void LinuxFile::Close() {
+  DCHECK_GT(fd_, 0);
+  int err = close(fd_);
+  if (err != 0) {
+    PLOG(WARNING) << "Problem closing " << name();
+  }
+  fd_ = kClosedFd;
+}
+
+
+bool LinuxFile::IsClosed() {
+  return fd_ == kClosedFd;
+}
+
+
+int64_t LinuxFile::Read(void* buffer, int64_t num_bytes) {
+  DCHECK_GE(fd_, 0);
+  return read(fd_, buffer, num_bytes);
+}
+
+
+int64_t LinuxFile::Write(const void* buffer, int64_t num_bytes) {
+  DCHECK_GE(fd_, 0);
+  return write(fd_, buffer, num_bytes);
+}
+
+
+off_t LinuxFile::Position() {
+  DCHECK_GE(fd_, 0);
+  return lseek(fd_, 0, SEEK_CUR);
+}
+
+
+off_t LinuxFile::Length() {
+  DCHECK_GE(fd_, 0);
+  off_t position = lseek(fd_, 0, SEEK_CUR);
+  if (position < 0) {
+    // The file is not capable of seeking. Return an error.
+    return -1;
+  }
+  off_t result = lseek(fd_, 0, SEEK_END);
+  lseek(fd_, position, SEEK_SET);
+  return result;
+}
+
+}  // namespace art
diff --git a/src/file_linux.h b/src/file_linux.h
new file mode 100644
index 0000000..7e36ada
--- /dev/null
+++ b/src/file_linux.h
@@ -0,0 +1,34 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_FILE_LINUX_H_
+#define ART_SRC_FILE_LINUX_H_
+
+#include "file.h"
+
+namespace art {
+
+class LinuxFile : public File {
+ public:
+  LinuxFile(const char* name, int fd, bool auto_close) :
+        File(name), fd_(fd), auto_close_(auto_close) {}
+  virtual ~LinuxFile();
+
+  virtual void Close();
+  virtual bool IsClosed();
+
+  virtual int64_t Read(void* buffer, int64_t num_bytes);
+  virtual int64_t Write(const void* buffer, int64_t num_bytes);
+
+  virtual off_t Length();
+  virtual off_t Position();
+
+ private:
+  static const int kClosedFd = -1;
+
+  int fd_;
+  bool auto_close_;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_FILE_LINUX_H_
diff --git a/src/file_test.cc b/src/file_test.cc
new file mode 100644
index 0000000..d019d09
--- /dev/null
+++ b/src/file_test.cc
@@ -0,0 +1,47 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+#include "common_test.h"
+#include "file.h"
+#include "os.h"
+#include "scoped_ptr.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+class FileTest : public RuntimeTest {};
+
+TEST_F(FileTest, Read) {
+  std::string filename = GetLibCoreDexFileName();
+  scoped_ptr<File> file(OS::OpenBinaryFile(filename.c_str(), false));
+  ASSERT_TRUE(file != NULL);
+  EXPECT_STREQ(filename.c_str(), file->name());
+  char buffer[3];
+  buffer[0] = '\0';
+  EXPECT_TRUE(file->ReadFully(buffer, 2));  // ReadFully returns true.
+  buffer[2] = '\0';
+  EXPECT_STREQ("PK", buffer); // zip file magic
+  EXPECT_FALSE(file->WriteByte(1));  // Cannot write to a read-only file.
+}
+
+
+TEST_F(FileTest, FileLength) {
+  std::string filename = GetLibCoreDexFileName();
+  scoped_ptr<File> file(OS::OpenBinaryFile(filename.c_str(), false));
+  ASSERT_TRUE(file != NULL);
+  EXPECT_NE(0, file->Length());
+}
+
+
+TEST_F(FileTest, FilePosition) {
+  std::string filename = GetLibCoreDexFileName();
+  scoped_ptr<File> file(OS::OpenBinaryFile(filename.c_str(), false));
+  ASSERT_TRUE(file != NULL);
+  char buf[4];
+  EXPECT_TRUE(file->ReadFully(buf, 2));
+  EXPECT_EQ(2, file->Position());
+  EXPECT_TRUE(file->ReadFully(buf, 2));
+  EXPECT_EQ(4, file->Position());
+}
+
+}  // namespace art
diff --git a/src/heap.h b/src/heap.h
index c71080e..698535f 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -24,11 +24,7 @@
 
   typedef void (RootVistor)(Object* root, void* arg);
 
-  static bool Init() {
-    return Init(kStartupSize, kMaximumSize);
-  }
-
-  static bool Init(size_t staring_size, size_t maximum_size);
+  static bool Init(size_t starting_size, size_t maximum_size);
 
   static void Destroy();
 
diff --git a/src/image_test.cc b/src/image_test.cc
new file mode 100644
index 0000000..80ccfa0
--- /dev/null
+++ b/src/image_test.cc
@@ -0,0 +1,28 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "common_test.h"
+#include "image_writer.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+class ImageTest : public RuntimeTest {};
+
+TEST_F(ImageTest, WriteRead) {
+  UseLibCoreDex();
+  scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
+  EXPECT_TRUE(libcore_dex_file.get() != NULL);
+
+  // 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());
+
+  ImageWriter writer;
+  ScratchFile tmp;
+  bool success = writer.Write(spaces[0], tmp.GetFilename(), reinterpret_cast<byte*>(0x5000000));
+  EXPECT_TRUE(success);
+}
+
+}  // namespace art
diff --git a/src/image_writer.cc b/src/image_writer.cc
new file mode 100644
index 0000000..0014724
--- /dev/null
+++ b/src/image_writer.cc
@@ -0,0 +1,132 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "image_writer.h"
+
+#include <sys/mman.h>
+#include <vector>
+
+#include "file.h"
+#include "globals.h"
+#include "heap.h"
+#include "logging.h"
+#include "object.h"
+#include "space.h"
+#include "utils.h"
+
+namespace art {
+
+bool ImageWriter::Write(Space* space, const char* filename, byte* image_base) {
+  image_base_ = image_base;
+  if (!Init(space)) {
+    return false;
+  }
+  CalculateNewObjectOffsets();
+  CopyAndFixupObjects();
+  
+  scoped_ptr<File> file(OS::OpenBinaryFile(filename, true));
+  if (file == NULL) {
+    return false;
+  }
+  return file->WriteFully(mem_map_->GetAddress(), top_);
+}
+
+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);
+  mem_map_.reset(MemMap::Map(length, prot, flags));
+  if (mem_map_ == NULL) {
+    PLOG(ERROR) << "mmap failed";
+    return false;
+  }
+  return true;
+}
+
+void ImageWriter::CalculateNewObjectOffsets() {
+  HeapBitmap* heap_bitmap = Heap::GetLiveBits();
+  DCHECK(heap_bitmap != NULL);
+  DCHECK_EQ(0U, top_);
+  top_ += sizeof(uint64_t);  // leave a header, ensures objects have non-zero offset for DCHECKs
+  heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);
+  DCHECK_LT(top_, mem_map_->GetLength());
+  // Note that top_ is left at end of used space
+}
+
+void ImageWriter::CalculateNewObjectOffsetsCallback(Object *obj, void *arg) {
+  DCHECK(obj != NULL);
+  DCHECK(arg != NULL);
+  ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
+  image_writer->SetImageOffset(obj, image_writer->top_);
+  image_writer->top_ += RoundUp(obj->Size(), 8);  // 64-bit alignment
+  DCHECK_LT(image_writer->top_, image_writer->mem_map_->GetLength());
+}
+
+void ImageWriter::CopyAndFixupObjects() {
+  HeapBitmap* heap_bitmap = Heap::GetLiveBits();
+  DCHECK(heap_bitmap != NULL);
+  heap_bitmap->Walk(CopyAndFixupObjectsCallback, this);
+}
+
+void ImageWriter::CopyAndFixupObjectsCallback(Object *obj, void *arg) {
+  DCHECK(obj != NULL);
+  DCHECK(arg != NULL);
+  ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
+  
+  size_t offset = image_writer->GetImageOffset(obj);
+  byte* dst = image_writer->mem_map_->GetAddress() + offset;
+  byte* src = reinterpret_cast<byte*>(obj);
+  size_t n = obj->Size();
+  DCHECK_LT(offset + n, image_writer->mem_map_->GetLength());
+  memcpy(dst, src, n);
+  Object* copy = reinterpret_cast<Object*>(dst);
+  image_writer->FixupObject(obj, copy);
+}
+
+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)
+  if (orig->IsObjectArray()) {
+    FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
+  } else {
+    FixupInstanceFields(orig, copy);
+  }
+}
+
+void ImageWriter::FixupObjectArray(ObjectArray<Object>* orig, ObjectArray<Object>* copy) {
+  for (size_t i = 0; i < orig->GetLength(); ++i) {
+    const Object* element = orig->Get(i);
+    copy->Set(i, GetImageAddress(element));
+  }
+}
+
+void ImageWriter::FixupInstanceFields(Object* orig, Object* copy) {
+  uint32_t ref_offsets = orig->GetClass()->GetReferenceOffsets();
+  if (ref_offsets != CLASS_WALK_SUPER) {
+    // Found a reference offset bitmap.  Fixup the specified offsets.
+    while (ref_offsets != 0) {
+      size_t right_shift = CLZ(ref_offsets);
+      size_t byte_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
+      const Object* ref = orig->GetFieldObject(byte_offset);
+      copy->SetFieldObject(byte_offset, GetImageAddress(ref));
+      ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
+    }
+  } else {
+    // There is no reference offset bitmap for this class.  Walk up
+    // the class inheritance hierarchy and find reference offsets the
+    // hard way.
+    for (Class *klass = orig->GetClass();
+         klass != NULL;
+         klass = klass->GetSuperClass()) {
+      for (size_t i = 0; i < klass->NumReferenceInstanceFields(); ++i) {
+        size_t field_offset = klass->GetInstanceField(i)->GetOffset();
+        const Object* ref = orig->GetFieldObject(field_offset);
+        copy->SetFieldObject(field_offset, GetImageAddress(ref));
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/src/image_writer.h b/src/image_writer.h
new file mode 100644
index 0000000..ac4a557
--- /dev/null
+++ b/src/image_writer.h
@@ -0,0 +1,68 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_IMAGE_H_
+#define ART_SRC_IMAGE_H_
+
+#include <cstddef>
+#include <stdint.h>
+
+#include "mem_map.h"
+#include "object.h"
+#include "os.h"
+#include "scoped_ptr.h"
+
+namespace art {
+
+class ImageWriter {
+
+ public:
+  ImageWriter() : top_(0), image_base_(NULL) {};
+  bool Write(Space* space, const char* filename, byte* image_base);
+  ~ImageWriter() {};
+
+ private:
+
+  bool Init(Space* space);
+
+  // we use the lock word to store the offset of the object in the image
+  void SetImageOffset(Object* object, size_t offset) {
+    DCHECK(object != NULL);
+    DCHECK(object->monitor_ == NULL);  // should be no lock
+    DCHECK_NE(0U, offset);
+    object->monitor_ = reinterpret_cast<Monitor*>(offset);
+  }
+  size_t GetImageOffset(const Object* object) {
+    DCHECK(object != NULL);
+    size_t offset = reinterpret_cast<size_t>(object->monitor_);
+    DCHECK_NE(0U, offset);
+    return offset;
+  }
+  Object* GetImageAddress(const Object* object) {
+    if (object == NULL) {
+      return NULL;
+    }
+    return reinterpret_cast<Object*>(image_base_ + GetImageOffset(object));
+  }
+
+  void CalculateNewObjectOffsets();
+  static void CalculateNewObjectOffsetsCallback(Object *obj, void *arg);
+
+  void CopyAndFixupObjects();
+  static void CopyAndFixupObjectsCallback(Object *obj, void *arg);
+  void FixupObject(Object* orig, Object* copy);
+  void FixupObjectArray(ObjectArray<Object>* orig, ObjectArray<Object>* copy);
+  void FixupInstanceFields(Object* orig, Object* copy);
+
+  // memory mapped for generating the image
+  scoped_ptr<MemMap> mem_map_;
+
+  // Offset to the free space in mem_map_ 
+  size_t top_;
+
+  // Target base address for the output image
+  byte* image_base_;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_IMAGE_H_
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index b7d8f5d..07aeec3 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -365,7 +365,7 @@
 }
 
 void* JniCompiler::AllocateCode(size_t size) {
-  CHECK_LT(((jni_code_top_ - jni_code_) + size), jni_code_size_);
+  CHECK_LT(((jni_code_top_ - jni_code_->GetAddress()) + size), jni_code_->GetLength());
   void *result = jni_code_top_;
   jni_code_top_ += size;
   return result;
@@ -374,18 +374,13 @@
 JniCompiler::JniCompiler() {
   // TODO: this shouldn't be managed by the JniCompiler, we should have a
   // code cache.
-  jni_code_size_ = kPageSize;
-  jni_code_ = static_cast<byte*>(mmap(NULL, jni_code_size_,
-                                      PROT_READ | PROT_WRITE | PROT_EXEC,
-                                      MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
-  CHECK_NE(MAP_FAILED, jni_code_);
-  jni_code_top_ = jni_code_;
+  jni_code_.reset(MemMap::Map(kPageSize,
+                              PROT_READ | PROT_WRITE | PROT_EXEC,
+                              MAP_ANONYMOUS | MAP_PRIVATE));
+  CHECK(jni_code_ !=  NULL);
+  jni_code_top_ = jni_code_->GetAddress();
 }
 
-JniCompiler::~JniCompiler() {
-  // TODO: this shouldn't be managed by the JniCompiler, we should have a
-  // code cache.
-  CHECK_EQ(0, munmap(jni_code_, jni_code_size_));
-}
+JniCompiler::~JniCompiler() {}
 
 }  // namespace art
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index c0b14cb..f1bd4e2 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -6,6 +6,8 @@
 #include "calling_convention.h"
 #include "globals.h"
 #include "macros.h"
+#include "mem_map.h"
+#include "scoped_ptr.h"
 
 namespace art {
 
@@ -31,11 +33,8 @@
   // A poor man's code cache
   void* AllocateCode(size_t size);
 
-  // Base of memory region for allocated code
-  byte* jni_code_;
-
-  // Allocated code size
-  size_t jni_code_size_;
+  // Allocated code
+  scoped_ptr<MemMap> jni_code_;
 
   // Pointer to the free space
   byte* jni_code_top_;
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 6211d8f..f717014 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -7,7 +7,9 @@
 #include "common_test.h"
 #include "dex_file.h"
 #include "jni_compiler.h"
+#include "mem_map.h"
 #include "runtime.h"
+#include "scoped_ptr.h"
 #include "thread.h"
 #include "gtest/gtest.h"
 
@@ -18,10 +20,10 @@
   virtual void SetUp() {
     RuntimeTest::SetUp();
     // Create thunk code that performs the native to managed transition
-    thunk_code_size_ = kPageSize;
-    thunk_ = mmap(NULL, thunk_code_size_, PROT_READ | PROT_WRITE | PROT_EXEC,
-                  MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-    CHECK_NE(MAP_FAILED, thunk_);
+    thunk_code_.reset(MemMap::Map(kPageSize,
+                                  PROT_READ | PROT_WRITE | PROT_EXEC,
+                                  MAP_ANONYMOUS | MAP_PRIVATE));
+    CHECK(thunk_code_ !=  NULL);
     Assembler thk_asm;
     // TODO: shouldn't have machine specific code in a general purpose file
 #if defined(__i386__)
@@ -62,7 +64,7 @@
 #error Unimplemented
 #endif
     size_t cs = thk_asm.CodeSize();
-    MemoryRegion code(thunk_, cs);
+    MemoryRegion code(thunk_code_->GetAddress(), cs);
     thk_asm.FinalizeInstructions(code);
     thunk_entry1_ = reinterpret_cast<jint (*)(const void*, art::Method*,
                                               Thread*, jobject, jint, jint,
@@ -77,7 +79,6 @@
   virtual void TearDown() {
     // Release thunk code
     CHECK(runtime_->DetachCurrentThread());
-    CHECK_EQ(0, munmap(thunk_, thunk_code_size_));
   }
 
   // Run generated code associated with method passing and returning int size
@@ -114,8 +115,7 @@
     return result;
   }
 
-  void* thunk_;
-  size_t thunk_code_size_;
+  scoped_ptr<MemMap> thunk_code_;
   jint (*thunk_entry1_)(const void*, Method*, Thread*, jobject, jint, jint,
                         jint);
   jdouble (*thunk_entry2_)(const void*, Method*, Thread*, jobject, jdouble,
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 6e61001..9c2093c 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -1892,6 +1892,7 @@
 };
 
 void MonitorEnterHelper(JNIEnv* env, jobject obj) {
+  env = Thread::Current()->GetJniEnv();  // XXX bdc do not commit workaround
   CHECK_EQ(Thread::Current()->GetJniEnv(), env);
   MonitorEnter(env, obj);  // Ignore the result.
 }
diff --git a/src/mark_stack.cc b/src/mark_stack.cc
index 47aaf0c..19dc1c1 100644
--- a/src/mark_stack.cc
+++ b/src/mark_stack.cc
@@ -22,13 +22,14 @@
 
 bool MarkStack::Init(size_t maximum_size) {
   size_t length = 64 * MB;
-  void* addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (addr == MAP_FAILED) {
+  mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS));
+  if (mem_map_ == NULL) {
     PLOG(ERROR) << "mmap failed";
     return false;
   }
+  byte* addr = mem_map_->GetAddress();
   base_ = reinterpret_cast<const Object**>(addr);
-  limit_ = reinterpret_cast<const Object**>((byte*)addr + length);
+  limit_ = reinterpret_cast<const Object**>(addr + length);
   ptr_ = reinterpret_cast<Object const**>(addr);
   int result = madvise(addr, length, MADV_DONTNEED);
   if (result == -1) {
@@ -37,11 +38,6 @@
   return true;
 }
 
-MarkStack::~MarkStack() {
-  int result = munmap((void*)base_, limit_ - base_);
-  if (result == -1) {
-    PLOG(WARNING) << "munmap failed";
-  }
-}
+MarkStack::~MarkStack() {}
 
 }  // namespace art
diff --git a/src/mark_stack.h b/src/mark_stack.h
index eeeb79c..38252c2 100644
--- a/src/mark_stack.h
+++ b/src/mark_stack.h
@@ -5,6 +5,8 @@
 
 #include "logging.h"
 #include "macros.h"
+#include "mem_map.h"
+#include "scoped_ptr.h"
 
 namespace art {
 
@@ -41,6 +43,9 @@
 
   bool Init(size_t maximum_size);
 
+  // Memory mapping of the mark stack.
+  scoped_ptr<MemMap> mem_map_;
+
   // Base of the mark stack.
   const Object* const* base_;
 
diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc
index 523b9e4..5d7c139 100644
--- a/src/mark_sweep.cc
+++ b/src/mark_sweep.cc
@@ -13,8 +13,6 @@
 #include "space.h"
 #include "thread.h"
 
-#define CLZ(x) __builtin_clz(x)
-
 namespace art {
 
 size_t MarkSweep::reference_referent_offset_ = 0;  // TODO
@@ -205,7 +203,7 @@
   DCHECK(obj->GetClass() != NULL);
   MarkObject(obj->GetClass());
   if (obj->IsObjectArray()) {
-    const ObjectArray<Object>* array = obj->AsObjectArray();
+    const ObjectArray<Object>* array = obj->AsObjectArray<Object>();
     for (size_t i = 0; i < array->GetLength(); ++i) {
       const Object* element = array->Get(i);
       MarkObject(element);
diff --git a/src/mem_map.h b/src/mem_map.h
new file mode 100644
index 0000000..4625f91
--- /dev/null
+++ b/src/mem_map.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_SRC_MEM_MAP_H_
+#define ART_SRC_MEM_MAP_H_
+
+#include <sys/mman.h>
+
+#include "utils.h"
+
+namespace art {
+
+// Used to keep track of mmap segments.
+class MemMap {
+ public:
+
+  // 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) {
+    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) {
+      return NULL;
+    }
+    return new MemMap(addr, length, addr, page_aligned_size);
+  }
+
+  // Map part of a file, taking care of non-page aligned offsets.  The
+  // "start" offset is absolute, not relative.
+  //
+  // 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) {
+    // 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) {
+      return NULL;
+    }
+    return new MemMap(addr+page_offset, length, addr, page_aligned_size);
+  }
+
+  ~MemMap() {
+    Unmap();
+  };
+
+  // Release a memory mapping, returning true on success or it was previously unmapped.
+  bool Unmap() {
+    if (base_addr_ == NULL && base_length_ == 0) {
+      return true;
+    }
+    int result = munmap(base_addr_, base_length_);
+    base_addr_ = NULL;
+    base_length_ = 0;
+    if (result == -1) {
+      return false;
+    }
+    return true;
+  }
+
+  byte* GetAddress() const {
+    return addr_;
+  }
+
+  size_t GetLength() const {
+    return 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) {
+    CHECK(addr_ != NULL);
+    CHECK(length_ != 0);
+    CHECK(base_addr_ != NULL);
+    CHECK(base_length_ != 0);
+  };
+
+  byte*  addr_;              // start of data
+  size_t length_;            // length of data
+
+  void*  base_addr_;         // page-aligned base address
+  size_t base_length_;       // length of mapping
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_MEM_MAP_H_
diff --git a/src/memory_region.h b/src/memory_region.h
index 3d04d91..f1a3ec5 100644
--- a/src/memory_region.h
+++ b/src/memory_region.h
@@ -76,8 +76,6 @@
 
   void* pointer_;
   size_t size_;
-
-  DISALLOW_COPY_AND_ASSIGN(MemoryRegion);
 };
 
 }  // namespace art
diff --git a/src/object.h b/src/object.h
index 927729c..cb7f379 100644
--- a/src/object.h
+++ b/src/object.h
@@ -118,11 +118,14 @@
   }
 
   Class* GetClass() const {
+    DCHECK(klass_ != NULL);
     return klass_;
   }
 
   bool InstanceOf(const Class* klass) const;
 
+  size_t Size() const;
+
   void MonitorEnter() {
     monitor_->Enter();
   }
@@ -152,12 +155,14 @@
   }
 
   const Object* GetFieldObject(size_t field_offset) const {
-    const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset;
-    return *reinterpret_cast<Object* const*>(raw_addr);
+    Object* that = const_cast<Object*>(this);
+    Object* other = that->GetFieldObject(field_offset);
+    return const_cast<const Object*>(other);
   }
 
   Object* GetFieldObject(size_t field_offset) {
-    return const_cast<Object*>(GetFieldObject(field_offset));
+    byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset;
+    return *reinterpret_cast<Object**>(raw_addr);
   }
 
   void SetFieldObject(size_t offset, Object* new_value) {
@@ -166,26 +171,30 @@
     // TODO: write barrier
   }
 
-  bool IsClass() const {
-    UNIMPLEMENTED(FATAL);
-    return true;
-  }
+  bool IsClass() const;
 
   Class* AsClass() {
+    DCHECK(IsClass());
     return down_cast<Class*>(this);
   }
 
   const Class* AsClass() const {
+    DCHECK(IsClass());
     return down_cast<const Class*>(this);
   }
 
-  bool IsObjectArray() const {
-    UNIMPLEMENTED(FATAL);
-    return true;
+  bool IsObjectArray() const;
+
+  template<class T>
+  ObjectArray<T>* AsObjectArray() {
+    DCHECK(IsObjectArray());
+    return down_cast<ObjectArray<T>*>(this);
   }
 
-  const ObjectArray<Object>* AsObjectArray() const {
-    return down_cast<const ObjectArray<Object>*>(this);
+  template<class T>
+  const ObjectArray<T>* AsObjectArray() const {
+    DCHECK(IsObjectArray());
+    return down_cast<const ObjectArray<T>*>(this);
   }
 
   bool IsReference() const {
@@ -213,9 +222,16 @@
     return true;
   }
 
-  bool IsArray() const {
-    UNIMPLEMENTED(FATAL);
-    return true;
+  bool IsArray() const;
+
+  Array* AsArray() {
+    DCHECK(IsArray());
+    return down_cast<Array*>(this);
+  }
+
+  const Array* AsArray() const {
+    DCHECK(IsArray());
+    return down_cast<const Array*>(this);
   }
 
  public:
@@ -578,17 +594,23 @@
 
 class Array : public Object {
  public:
+  static size_t Size(size_t component_count,
+                     size_t component_size) {
+    return sizeof(Array) + component_count * component_size;
+  }
   static Array* Alloc(Class* array_class,
                       size_t component_count,
                       size_t component_size) {
-    size_t size = sizeof(Array) + component_count * component_size;
-    Array* array = down_cast<Array*>(Heap::AllocObject(array_class, size));
+    size_t size = Size(component_count, component_size);
+    Array* array = Heap::AllocObject(array_class, size)->AsArray();
     if (array != NULL) {
       array->SetLength(component_count);
     }
     return array;
   }
 
+  size_t Size() const;
+
   uint32_t GetLength() const {
     return length_;
   }
@@ -611,9 +633,7 @@
  public:
   static ObjectArray<T>* Alloc(Class* object_array_class,
                                size_t length) {
-    return down_cast<ObjectArray<T>*>(Array::Alloc(object_array_class,
-                                                   length,
-                                                   sizeof(uint32_t)));
+    return Array::Alloc(object_array_class, length, sizeof(uint32_t))->AsObjectArray<T>();
   }
 
   T* const * GetData() const {
@@ -775,6 +795,24 @@
     return component_type_;
   }
 
+  size_t GetComponentSize() const {
+    switch (component_type_->descriptor_[0]) {
+      case 'B': return 1;  // byte
+      case 'C': return 2;  // char
+      case 'D': return 8;  // double
+      case 'F': return 4;  // float
+      case 'I': return 4;  // int
+      case 'J': return 8;  // long
+      case 'S': return 2;  // short
+      case 'Z': return 1;  // boolean
+      case 'L': return sizeof(Object*);
+      case '[': return sizeof(Array*);
+      default: 
+        LOG(ERROR) << "Unknown component type " << component_type_->descriptor_;
+        return 0;
+    }
+  }
+
   const StringPiece& GetDescriptor() const {
     DCHECK_NE(0, descriptor_.size());
     return descriptor_;
@@ -1115,6 +1153,28 @@
   return klass->IsAssignableFrom(klass_);
 }
 
+inline bool Object::IsClass() const {
+  return klass_ == klass_->klass_;
+}
+
+inline bool Object::IsObjectArray() const {
+  return IsArray() && !klass_->component_type_->IsPrimitive();
+}
+
+inline bool Object::IsArray() const {
+  return klass_->IsArray();
+}
+
+inline size_t Object::Size() const {
+  if (IsArray()) {
+    return AsArray()->Size();
+  }
+  return klass_->object_size_;
+}
+
+inline size_t Array::Size() const {
+  return Size(GetLength(), klass_->GetComponentSize());
+}
 
 class DataObject : public Object {
  public:
diff --git a/src/object_bitmap.cc b/src/object_bitmap.cc
index 85fee73..fc3c2ee4 100644
--- a/src/object_bitmap.cc
+++ b/src/object_bitmap.cc
@@ -21,8 +21,6 @@
 
 namespace art {
 
-#define CLZ(x) __builtin_clz(x)
-
 HeapBitmap* HeapBitmap::Create(byte* base, size_t length) {
   scoped_ptr<HeapBitmap> bitmap(new HeapBitmap(base, length));
   if (!bitmap->Init(base, length)) {
@@ -38,12 +36,12 @@
 bool HeapBitmap::Init(const byte* base, size_t max_size) {
   CHECK(base != NULL);
   size_t length = HB_OFFSET_TO_INDEX(max_size) * kWordSize;
-  void* words = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (words == MAP_FAILED) {
+  mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS));
+  if (mem_map_ == NULL) {
     LOG(ERROR) << "mmap failed";
     return false;
   }
-  words_ = static_cast<unsigned long*>(words);
+  words_ = reinterpret_cast<word*>(mem_map_->GetAddress());
   num_bytes_ = length;
   base_ = reinterpret_cast<uintptr_t>(base);
   max_ = base_ - 1;
@@ -51,15 +49,7 @@
 }
 
 // Clean up any resources associated with the bitmap.
-HeapBitmap::~HeapBitmap() {
-  if (words_ != NULL) {
-    int result = munmap(words_, num_bytes_);
-    if (result == -1) {
-      PLOG(WARNING) << "munmap failed";
-    }
-    words_ = NULL;
-  }
-}
+HeapBitmap::~HeapBitmap() {}
 
 // Fill the bitmap with zeroes.  Returns the bitmap's memory to the
 // system as a side-effect.
@@ -169,8 +159,8 @@
   void** pb = pointer_buf;
   size_t start = HB_OFFSET_TO_INDEX(base - live_bitmap.base_);
   size_t end = HB_OFFSET_TO_INDEX(max - live_bitmap.base_);
-  unsigned long* live = live_bitmap.words_;
-  unsigned long* mark = mark_bitmap.words_;
+  word* live = live_bitmap.words_;
+  word* mark = mark_bitmap.words_;
   for (size_t i = start; i <= end; i++) {
     unsigned long garbage = live[i] & ~mark[i];
     if (garbage != 0) {
diff --git a/src/object_bitmap.h b/src/object_bitmap.h
index d766a83..3cc973c 100644
--- a/src/object_bitmap.h
+++ b/src/object_bitmap.h
@@ -20,6 +20,8 @@
 
 #include "globals.h"
 #include "logging.h"
+#include "mem_map.h"
+#include "scoped_ptr.h"
 
 namespace art {
 
@@ -117,7 +119,9 @@
 
   bool Init(const byte* base, size_t length);
 
-  unsigned long* words_;
+  scoped_ptr<MemMap> mem_map_;
+
+  word* words_;
 
   size_t num_bytes_;
 
diff --git a/src/os.h b/src/os.h
new file mode 100644
index 0000000..440c997
--- /dev/null
+++ b/src/os.h
@@ -0,0 +1,28 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_OS_H_
+#define ART_SRC_OS_H_
+
+namespace art {
+
+// Interface to the underlying OS platform.
+
+class File;
+
+class OS {
+ public:
+
+  // Open a file. The returned file must be deleted by the caller.
+  static File* OpenBinaryFile(const char* name, bool writable);
+  static File* OpenTextFile(const char* name, bool writable);
+
+  // Create a file from an already open file descriptor
+  static File* FileFromFd(const char* name, int fd);
+
+  // Check if a file exists.
+  static bool FileExists(const char* name);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_OS_H_
diff --git a/src/os_linux.cc b/src/os_linux.cc
new file mode 100644
index 0000000..da8ea38
--- /dev/null
+++ b/src/os_linux.cc
@@ -0,0 +1,43 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+#include "os.h"
+
+#include <cstddef>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "file_linux.h"
+
+namespace art {
+
+File* OS::OpenBinaryFile(const char* name, bool writable) {
+  int flags = O_RDONLY;
+  if (writable) {
+    flags = (O_RDWR | O_CREAT | O_TRUNC);
+  }
+  int fd = open(name, flags, 0666);
+  if (fd < 0) {
+    return NULL;
+  }
+  return new LinuxFile(name, fd, true);
+}
+
+File* OS::OpenTextFile(const char* name, bool writable) {
+  return OpenBinaryFile(name, writable);
+}
+
+File* OS::FileFromFd(const char* name, int fd) {
+  return new LinuxFile(name, fd, false);
+}
+
+bool OS::FileExists(const char* name) {
+  struct stat st;
+  if (stat(name, &st) == 0) {
+    return S_ISREG(st.st_mode);  // TODO Deal with symlinks?
+  } else {
+    return false;
+  }
+}
+
+}  // namespace art
diff --git a/src/runtime_test.cc b/src/runtime_test.cc
index 4b59cb3..3949ac8 100644
--- a/src/runtime_test.cc
+++ b/src/runtime_test.cc
@@ -1,6 +1,6 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
-#include "src/runtime.h"
+#include "runtime.h"
 
 #include "gtest/gtest.h"
 
diff --git a/src/scoped_ptr.h b/src/scoped_ptr.h
index c76a64e..4d9b9b6 100644
--- a/src/scoped_ptr.h
+++ b/src/scoped_ptr.h
@@ -18,9 +18,9 @@
 //  implementation of the scoped_ptr class, and its closely-related brethren,
 //  scoped_array, scoped_ptr_malloc, and make_scoped_ptr.
 
+#include "logging.h"
 #include "macros.h"
 
-#include <assert.h>
 #include <stdlib.h>
 
 #include <algorithm>
@@ -71,13 +71,13 @@
   }
 
   // Accessors to get the owned object.
-  // operator* and operator-> will assert() if there is no current object.
+  // operator* and operator-> will DCHECK() if there is no current object.
   C& operator*() const {
-    assert(ptr_ != NULL);
+    DCHECK(ptr_ != NULL);
     return *ptr_;
   }
   C* operator->() const  {
-    assert(ptr_ != NULL);
+    DCHECK(ptr_ != NULL);
     return ptr_;
   }
   C* get() const { return ptr_; }
@@ -190,10 +190,10 @@
   }
 
   // Get one element of the current object.
-  // Will assert() if there is no current object, or index i is negative.
+  // Will DCHECK() if there is no current object, or index i is negative.
   C& operator[](std::ptrdiff_t i) const {
-    assert(i >= 0);
-    assert(array_ != NULL);
+    DCHECK_GE(i, 0);
+    DCHECK(array_ != NULL);
     return array_[i];
   }
 
@@ -295,15 +295,15 @@
   }
 
   // Get the current object.
-  // operator* and operator-> will cause an assert() failure if there is
+  // operator* and operator-> will cause an DCHECK() failure if there is
   // no current object.
   C& operator*() const {
-    assert(ptr_ != NULL);
+    DCHECK(ptr_ != NULL);
     return *ptr_;
   }
 
   C* operator->() const {
-    assert(ptr_ != NULL);
+    DCHECK(ptr_ != NULL);
     return ptr_;
   }
 
diff --git a/src/space.cc b/src/space.cc
index 9f01838..c3d763c 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -48,30 +48,22 @@
   size_t length = RoundUp(maximum_size_, kPageSize);
   int prot = PROT_READ | PROT_WRITE;
   int flags = MAP_PRIVATE | MAP_ANONYMOUS;
-  void* base = mmap(NULL, length, prot, flags, -1, 0);
-  if (base == MAP_FAILED) {
+  mem_map_.reset(MemMap::Map(length, prot, flags));
+  if (mem_map_ == NULL) {
     PLOG(ERROR) << "mmap failed";
     return false;
   }
-  base_ = static_cast<byte*>(base);
+  base_ = mem_map_->GetAddress();
   limit_ = base_ + length;
-  mspace_ = CreateMallocSpace(base, startup_size_, maximum_size_);
+  mspace_ = CreateMallocSpace(base_, startup_size_, maximum_size_);
   if (mspace_ == NULL) {
-    munmap(base_, length);
+    mem_map_->Unmap();
     return false;
   }
   return true;
 }
 
-Space::~Space() {
-  if (base_ == NULL) {
-    return;
-  }
-  int result = munmap(base_, limit_ - base_);
-  if (result == -1) {
-    PLOG(WARNING) << "munmap failed";
-  }
-}
+Space::~Space() {}
 
 Object* Space::AllocWithoutGrowth(size_t num_bytes) {
   return reinterpret_cast<Object*>(mspace_calloc(mspace_, 1, num_bytes));
diff --git a/src/space.h b/src/space.h
index 3cfa505..7b72bba 100644
--- a/src/space.h
+++ b/src/space.h
@@ -5,6 +5,8 @@
 
 #include "globals.h"
 #include "macros.h"
+#include "mem_map.h"
+#include "scoped_ptr.h"
 
 namespace art {
 
@@ -29,21 +31,21 @@
 
   void Grow(size_t num_bytes);
 
-  byte* GetBase() {
+  byte* GetBase() const {
     return base_;
   }
 
-  byte* GetLimit() {
+  byte* GetLimit() const {
     return limit_;
   }
 
-  size_t Size() {
+  size_t Size() const {
     return limit_ - base_;
   }
 
   size_t AllocationSize(const Object* obj);
 
-  bool IsCondemned() {
+  bool IsCondemned() const {
     return true;  // TODO
   }
 
@@ -68,6 +70,8 @@
 
   void* mspace_;
 
+  scoped_ptr<MemMap> mem_map_;
+
   byte* base_;
 
   byte* limit_;
diff --git a/src/utils.h b/src/utils.h
index ddecef9..842db63 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -4,6 +4,7 @@
 #define ART_SRC_UTILS_H_
 
 #include "globals.h"
+#include "logging.h"
 #include "stringprintf.h"
 
 namespace art {
@@ -96,6 +97,8 @@
   return static_cast<int>(x & 0x0000003F);
 }
 
+#define CLZ(x) __builtin_clz(x)
+
 static inline bool NeedsEscaping(uint16_t ch) {
   return (ch < ' ' || ch > '~');
 }
diff --git a/src/zip_archive.cc b/src/zip_archive.cc
index 2c63150..fe263ba 100644
--- a/src/zip_archive.cc
+++ b/src/zip_archive.cc
@@ -24,13 +24,13 @@
 namespace art {
 
 // Get 2 little-endian bytes.
-static uint32_t Le16ToHost(const uint8_t* src) {
+static uint32_t Le16ToHost(const byte* src) {
   return ((src[0] <<  0) |
           (src[1] <<  8));
 }
 
 // Get 4 little-endian bytes.
-static uint32_t Le32ToHost(const uint8_t* src) {
+static uint32_t Le32ToHost(const byte* src) {
   return ((src[0] <<  0) |
           (src[1] <<  8) |
           (src[2] << 16) |
@@ -114,22 +114,7 @@
   return data_offset;
 }
 
-// Write until all bytes have been written, returning true on success
-bool WriteFully(int fd, const uint8_t* buf, size_t count) {
-  while (count != 0) {
-    ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
-    if (actual < 0) {
-      return false;
-    }
-    if (actual != static_cast<ssize_t>(count)) {
-      buf += actual;
-    }
-    count -= actual;
-  }
-  return true;
-}
-
-static bool CopyFdToFd(int out, int in, size_t count) {
+static bool CopyFdToFile(File& file, int in, size_t count) {
   const size_t kBufSize = 32768;
   uint8_t buf[kBufSize];
 
@@ -139,7 +124,7 @@
     if (actual != static_cast<ssize_t>(bytes_to_read)) {
       return false;
     }
-    if (!WriteFully(out, buf, bytes_to_read)) {
+    if (!file.WriteFully(buf, bytes_to_read)) {
       return false;
     }
     count -= bytes_to_read;
@@ -149,7 +134,7 @@
 
 class ZStream {
  public:
-  ZStream(uint8_t* write_buf, size_t write_buf_size) {
+  ZStream(byte* write_buf, size_t write_buf_size) {
     // Initialize the zlib stream struct.
     memset(&zstream_, 0, sizeof(zstream_));
     zstream_.zalloc = Z_NULL;
@@ -173,7 +158,7 @@
   z_stream zstream_;
 };
 
-static bool InflateToFd(int out, int in, size_t uncompressed_length, size_t compressed_length) {
+static bool InflateToFile(File& out, int in, size_t uncompressed_length, size_t compressed_length) {
   const size_t kBufSize = 32768;
   scoped_array<uint8_t> read_buf(new uint8_t[kBufSize]);
   scoped_array<uint8_t> write_buf(new uint8_t[kBufSize]);
@@ -227,7 +212,7 @@
     if (zstream->Get().avail_out == 0 ||
         (zerr == Z_STREAM_END && zstream->Get().avail_out != kBufSize)) {
       size_t bytes_to_write = zstream->Get().next_out - write_buf.get();
-      if (!WriteFully(out, write_buf.get(), bytes_to_write)) {
+      if (!out.WriteFully(write_buf.get(), bytes_to_write)) {
         return false;
       }
       zstream->Get().next_out = write_buf.get();
@@ -247,7 +232,7 @@
   return true;
 }
 
-bool ZipEntry::Extract(int fd) {
+bool ZipEntry::Extract(File& file) {
 
   off_t data_offset = GetDataOffset();
   if (data_offset == -1) {
@@ -262,9 +247,9 @@
   // for uncompressed data).
   switch (GetCompressionMethod()) {
     case kCompressStored:
-      return CopyFdToFd(fd, zip_archive_->fd_, GetUncompressedLength());
+      return CopyFdToFile(file, zip_archive_->fd_, GetUncompressedLength());
     case kCompressDeflated:
-      return InflateToFd(fd, zip_archive_->fd_, GetUncompressedLength(), GetCompressedLength());
+      return InflateToFile(file, zip_archive_->fd_, GetUncompressedLength(), GetCompressedLength());
     default:
       return false;
   }
@@ -376,7 +361,7 @@
   }
 
   off_t eocd_offset = search_start + i;
-  const uint8_t* eocd_ptr = scan_buf.get() + i;
+  const byte* eocd_ptr = scan_buf.get() + i;
 
   DCHECK(eocd_offset < file_length);
 
@@ -399,7 +384,7 @@
   }
 
   // It all looks good.  Create a mapping for the CD.
-  dir_map_.reset(MemMap::Map(fd_, dir_offset, dir_size));
+  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;
@@ -411,12 +396,12 @@
 }
 
 bool ZipArchive::Parse() {
-  const uint8_t* cd_ptr = reinterpret_cast<const uint8_t*>(dir_map_->GetAddress());
+  const byte* cd_ptr = dir_map_->GetAddress();
   size_t cd_length = dir_map_->GetLength();
 
   // Walk through the central directory, adding entries to the hash
   // table and verifying values.
-  const uint8_t* ptr = cd_ptr;
+  const byte* ptr = cd_ptr;
   for (int i = 0; i < num_entries_; i++) {
     if (Le32ToHost(ptr) != kCDESignature) {
       LOG(WARNING) << "Zip: missed a central dir sig (at " << i << ")";
diff --git a/src/zip_archive.h b/src/zip_archive.h
index 858afe09..8001fa3 100644
--- a/src/zip_archive.h
+++ b/src/zip_archive.h
@@ -22,8 +22,10 @@
 #include <sys/mman.h>
 #include <zlib.h>
 
+#include "file.h"
 #include "globals.h"
 #include "logging.h"
+#include "mem_map.h"
 #include "scoped_ptr.h"
 #include "stringpiece.h"
 #include "unordered_map.h"
@@ -37,13 +39,13 @@
 
  public:
   // Uncompress an entry, in its entirety, to an open file descriptor.
-  bool Extract(int fd);
+  bool Extract(File& file);
 
   uint32_t GetCrc32();
 
  private:
 
-  ZipEntry(ZipArchive* zip_archive, const uint8_t* ptr) : zip_archive_(zip_archive), ptr_(ptr) {};
+  ZipEntry(ZipArchive* zip_archive, const byte* ptr) : zip_archive_(zip_archive), ptr_(ptr) {};
 
   // Zip compression methods
   enum {
@@ -64,78 +66,11 @@
   ZipArchive* zip_archive_;
 
   // pointer to zip entry within central directory
-  const uint8_t* ptr_;
+  const byte* ptr_;
 
   friend class ZipArchive;
 };
 
-// Used to keep track of unaligned mmap segments.
-class MemMap {
- public:
-
-  // Map part of a file into a shared, read-only memory segment.  The "start"
-  // offset is absolute, not relative.
-  //
-  // On success, returns returns a MemMap instance.  On failure, returns a NULL;
-  static MemMap* Map(int fd, off_t start, size_t length) {
-    // 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;
-    uint8_t* addr = reinterpret_cast<uint8_t*>(mmap(NULL,
-                                                    page_aligned_size,
-                                                    PROT_READ,
-                                                    MAP_FILE | MAP_SHARED,
-                                                    fd,
-                                                    page_aligned_offset));
-    if (addr == MAP_FAILED) {
-      return NULL;
-    }
-    return new MemMap(addr+page_offset, length, addr, page_aligned_size);
-  }
-
-  ~MemMap() {
-    Unmap();
-  };
-
-  // Release a memory mapping, returning true on success or it was previously unmapped.
-  bool Unmap() {
-    if (base_addr_ == NULL && base_length_ == 0) {
-      return true;
-    }
-    int result = munmap(base_addr_, base_length_);
-    if (result != 0) {
-      return false;
-    }
-    base_addr_ = NULL;
-    base_length_ = 0;
-    return true;
-  }
-
-  void* GetAddress() {
-    return addr_;
-  }
-
-  size_t GetLength() {
-    return length_;
-  }
-
- private:
-  MemMap(void* addr, size_t length, void* base_addr, size_t base_length)
-      : addr_(addr), length_(length), base_addr_(base_addr), base_length_(base_length) {
-    CHECK(addr_ != NULL);
-    CHECK(length_ != 0);
-    CHECK(base_addr_ != NULL);
-    CHECK(base_length_ != 0);
-  };
-
-  void*   addr_;              // start of data
-  size_t  length_;            // length of data
-
-  void*   base_addr_;         // page-aligned base address
-  size_t  base_length_;       // length of mapping
-};
-
 class ZipArchive {
  public:
 
@@ -184,7 +119,7 @@
   uint16_t num_entries_;
   off_t dir_offset_;
   scoped_ptr<MemMap> dir_map_;
-  typedef std::tr1::unordered_map<StringPiece, const uint8_t*> DirEntries;
+  typedef std::tr1::unordered_map<StringPiece, const byte*> DirEntries;
   DirEntries dir_entries_;
 
   friend class ZipEntry;
diff --git a/src/zip_archive_test.cc b/src/zip_archive_test.cc
index 788e8fc..74b3795 100644
--- a/src/zip_archive_test.cc
+++ b/src/zip_archive_test.cc
@@ -5,6 +5,7 @@
 #include <sys/types.h>
 
 #include "common_test.h"
+#include "os.h"
 #include "zip_archive.h"
 #include "gtest/gtest.h"
 
@@ -12,47 +13,17 @@
 
 class ZipArchiveTest : public RuntimeTest {};
 
-class TmpFile {
- public:
-  TmpFile() {
-    std::string filename_template;
-    filename_template = getenv("ANDROID_DATA");
-    filename_template += "/TmpFile-XXXXXX";
-    filename_.reset(strdup(filename_template.c_str()));
-    CHECK(filename_ != NULL);
-    fd_ = mkstemp(filename_.get());
-    CHECK_NE(-1, fd_);
-  }
-
-  ~TmpFile() {
-    int unlink_result = unlink(filename_.get());
-    CHECK_EQ(0, unlink_result);
-    int close_result = close(fd_);
-    CHECK_EQ(0, close_result);
-  }
-
-  const char* GetFilename() const {
-    return filename_.get();
-  }
-
-  int GetFd() const {
-    return fd_;
-  }
-
- private:
-  scoped_ptr_malloc<char> filename_;
-  int fd_;
-};
-
 TEST_F(ZipArchiveTest, FindAndExtract) {
   scoped_ptr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName()));
   ASSERT_TRUE(zip_archive != false);
   scoped_ptr<ZipEntry> zip_entry(zip_archive->Find("classes.dex"));
   ASSERT_TRUE(zip_entry != false);
 
-  TmpFile tmp;
+  ScratchFile tmp;
   ASSERT_NE(-1, tmp.GetFd());
-  bool success = zip_entry->Extract(tmp.GetFd());
+  scoped_ptr<File> file(OS::FileFromFd(tmp.GetFilename(), tmp.GetFd()));
+  ASSERT_TRUE(file != NULL);
+  bool success = zip_entry->Extract(*file);
   ASSERT_TRUE(success);
   close(tmp.GetFd());