Merge "Simplify boolean condition compared to 0"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index dcde5ab..717403f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -29,6 +29,7 @@
   GetMethodSignature \
   Instrumentation \
   Interfaces \
+  LambdaInterfaces \
   Lookup \
   Main \
   MultiDex \
@@ -77,6 +78,7 @@
 ART_GTEST_oat_test_DEX_DEPS := Main
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
 ART_GTEST_proxy_test_DEX_DEPS := Interfaces
+ART_GTEST_lambda_proxy_test_DEX_DEPS := LambdaInterfaces
 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
 ART_GTEST_stub_test_DEX_DEPS := AllFields
 ART_GTEST_transaction_test_DEX_DEPS := Transaction
@@ -97,6 +99,7 @@
 
 # TODO: document why this is needed.
 ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_lambda_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
 
 # The dexdump test requires an image and the dexdump utility.
 # TODO: rename into dexdump when migration completes
@@ -233,6 +236,7 @@
 
 COMPILER_GTEST_COMMON_SRC_FILES := \
   runtime/jni_internal_test.cc \
+  runtime/lambda_proxy_test.cc \
   runtime/proxy_test.cc \
   runtime/reflection_test.cc \
   compiler/compiled_method_test.cc \
@@ -741,6 +745,7 @@
 ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
 ART_GTEST_object_test_DEX_DEPS :=
 ART_GTEST_proxy_test_DEX_DEPS :=
+ART_GTEST_lambda_proxy_test_DEX_DEPS :=
 ART_GTEST_reflection_test_DEX_DEPS :=
 ART_GTEST_stub_test_DEX_DEPS :=
 ART_GTEST_transaction_test_DEX_DEPS :=
diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/dwarf/debug_info_entry_writer.h
index d9b367b..aa31036 100644
--- a/compiler/dwarf/debug_info_entry_writer.h
+++ b/compiler/dwarf/debug_info_entry_writer.h
@@ -20,6 +20,7 @@
 #include <cstdint>
 #include <unordered_map>
 
+#include "base/casts.h"
 #include "dwarf/dwarf_constants.h"
 #include "dwarf/writer.h"
 #include "leb128.h"
@@ -47,9 +48,9 @@
  * It also handles generation of abbreviations.
  *
  * Usage:
- *   StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
+ *   StartTag(DW_TAG_compile_unit);
  *     WriteStrp(DW_AT_producer, "Compiler name", debug_str);
- *     StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
+ *     StartTag(DW_TAG_subprogram);
  *       WriteStrp(DW_AT_name, "Foo", debug_str);
  *     EndTag();
  *   EndTag();
@@ -59,36 +60,40 @@
   static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
 
  public:
+  static constexpr size_t kCompilationUnitHeaderSize = 11;
+
   // Start debugging information entry.
-  void StartTag(Tag tag, Children children) {
-    DCHECK(has_children) << "This tag can not have nested tags";
+  // Returns offset of the entry in compilation unit.
+  size_t StartTag(Tag tag) {
     if (inside_entry_) {
       // Write abbrev code for the previous entry.
-      this->UpdateUleb128(abbrev_code_offset_, EndAbbrev());
+      // Parent entry is finalized before any children are written.
+      this->UpdateUleb128(abbrev_code_offset_, EndAbbrev(DW_CHILDREN_yes));
       inside_entry_ = false;
     }
-    StartAbbrev(tag, children);
+    StartAbbrev(tag);
     // Abbrev code placeholder of sufficient size.
     abbrev_code_offset_ = this->data()->size();
     this->PushUleb128(NextAbbrevCode());
     depth_++;
     inside_entry_ = true;
-    has_children = (children == DW_CHILDREN_yes);
+    return abbrev_code_offset_ + kCompilationUnitHeaderSize;
   }
 
   // End debugging information entry.
   void EndTag() {
     DCHECK_GT(depth_, 0);
     if (inside_entry_) {
-      // Write abbrev code for this tag.
-      this->UpdateUleb128(abbrev_code_offset_, EndAbbrev());
+      // Write abbrev code for this entry.
+      this->UpdateUleb128(abbrev_code_offset_, EndAbbrev(DW_CHILDREN_no));
       inside_entry_ = false;
-    }
-    if (has_children) {
-      this->PushUint8(0);  // End of children.
+      // This entry has no children and so there is no terminator.
+    } else {
+      // The entry has been already finalized so it must be parent entry
+      // and we need to write the terminator required by DW_CHILDREN_yes.
+      this->PushUint8(0);
     }
     depth_--;
-    has_children = true;  // Parent tag obviously has children.
   }
 
   void WriteAddr(Attribute attrib, uint64_t value) {
@@ -101,10 +106,10 @@
     }
   }
 
-  void WriteBlock(Attribute attrib, const void* ptr, int size) {
+  void WriteBlock(Attribute attrib, const void* ptr, size_t num_bytes) {
     AddAbbrevAttribute(attrib, DW_FORM_block);
-    this->PushUleb128(size);
-    this->PushData(ptr, size);
+    this->PushUleb128(num_bytes);
+    this->PushData(ptr, num_bytes);
   }
 
   void WriteData1(Attribute attrib, uint8_t value) {
@@ -147,12 +152,12 @@
     this->PushUint8(value ? 1 : 0);
   }
 
-  void WriteRef4(Attribute attrib, int cu_offset) {
+  void WriteRef4(Attribute attrib, uint32_t cu_offset) {
     AddAbbrevAttribute(attrib, DW_FORM_ref4);
     this->PushUint32(cu_offset);
   }
 
-  void WriteRef(Attribute attrib, int cu_offset) {
+  void WriteRef(Attribute attrib, uint32_t cu_offset) {
     AddAbbrevAttribute(attrib, DW_FORM_ref_udata);
     this->PushUleb128(cu_offset);
   }
@@ -162,16 +167,21 @@
     this->PushString(value);
   }
 
-  void WriteStrp(Attribute attrib, int address) {
+  void WriteStrp(Attribute attrib, size_t debug_str_offset) {
     AddAbbrevAttribute(attrib, DW_FORM_strp);
-    this->PushUint32(address);
+    this->PushUint32(dchecked_integral_cast<uint32_t>(debug_str_offset));
   }
 
-  void WriteStrp(Attribute attrib, const char* value, std::vector<uint8_t>* debug_str) {
+  void WriteStrp(Attribute attrib, const char* str, size_t len,
+                 std::vector<uint8_t>* debug_str) {
     AddAbbrevAttribute(attrib, DW_FORM_strp);
-    int address = debug_str->size();
-    debug_str->insert(debug_str->end(), value, value + strlen(value) + 1);
-    this->PushUint32(address);
+    this->PushUint32(debug_str->size());
+    debug_str->insert(debug_str->end(), str, str + len);
+    debug_str->push_back(0);
+  }
+
+  void WriteStrp(Attribute attrib, const char* str, std::vector<uint8_t>* debug_str) {
+    WriteStrp(attrib, str, strlen(str), debug_str);
   }
 
   bool Is64bit() const { return is64bit_; }
@@ -180,7 +190,11 @@
     return patch_locations_;
   }
 
+  int Depth() const { return depth_; }
+
   using Writer<Vector>::data;
+  using Writer<Vector>::size;
+  using Writer<Vector>::UpdateUint32;
 
   DebugInfoEntryWriter(bool is64bitArch,
                        Vector* debug_abbrev,
@@ -196,16 +210,17 @@
   }
 
   ~DebugInfoEntryWriter() {
+    DCHECK(!inside_entry_);
     DCHECK_EQ(depth_, 0);
   }
 
  private:
   // Start abbreviation declaration.
-  void StartAbbrev(Tag tag, Children children) {
-    DCHECK(!inside_entry_);
+  void StartAbbrev(Tag tag) {
     current_abbrev_.clear();
     EncodeUnsignedLeb128(&current_abbrev_, tag);
-    current_abbrev_.push_back(children);
+    has_children_offset_ = current_abbrev_.size();
+    current_abbrev_.push_back(0);  // Place-holder for DW_CHILDREN.
   }
 
   // Add attribute specification.
@@ -220,8 +235,9 @@
   }
 
   // End abbreviation declaration and return its code.
-  int EndAbbrev() {
-    DCHECK(inside_entry_);
+  int EndAbbrev(Children has_children) {
+    DCHECK(!current_abbrev_.empty());
+    current_abbrev_[has_children_offset_] = has_children;
     auto it = abbrev_codes_.insert(std::make_pair(std::move(current_abbrev_),
                                                   NextAbbrevCode()));
     int abbrev_code = it.first->second;
@@ -241,6 +257,7 @@
   // Fields for writing and deduplication of abbrevs.
   Writer<Vector> debug_abbrev_;
   Vector current_abbrev_;
+  size_t has_children_offset_ = 0;
   std::unordered_map<Vector, int,
                      FNVHash<Vector> > abbrev_codes_;
 
@@ -250,7 +267,6 @@
   int depth_ = 0;
   size_t abbrev_code_offset_ = 0;  // Location to patch once we know the code.
   bool inside_entry_ = false;  // Entry ends at first child (if any).
-  bool has_children = true;
   std::vector<uintptr_t> patch_locations_;
 };
 
diff --git a/compiler/dwarf/dedup_vector.h b/compiler/dwarf/dedup_vector.h
new file mode 100644
index 0000000..7fb21b7
--- /dev/null
+++ b/compiler/dwarf/dedup_vector.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_DWARF_DEDUP_VECTOR_H_
+#define ART_COMPILER_DWARF_DEDUP_VECTOR_H_
+
+#include <vector>
+#include <unordered_map>
+
+namespace art {
+namespace dwarf {
+  class DedupVector {
+   public:
+    // Returns an offset to previously inserted identical block of data,
+    // or appends the data at the end of the vector and returns offset to it.
+    size_t Insert(const uint8_t* ptr, size_t num_bytes) {
+      // See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+      uint32_t hash = 2166136261u;
+      for (size_t i = 0; i < num_bytes; i++) {
+        hash = (hash ^ ptr[i]) * 16777619u;
+      }
+      // Try to find existing copy of the data.
+      const auto& range = hash_to_offset_.equal_range(hash);
+      for (auto it = range.first; it != range.second; ++it) {
+        const size_t offset = it->second;
+        if (offset + num_bytes <= vector_.size() &&
+            memcmp(vector_.data() + offset, ptr, num_bytes) == 0) {
+          return offset;
+        }
+      }
+      // Append the data at the end of the vector.
+      const size_t new_offset = vector_.size();
+      hash_to_offset_.emplace(hash, new_offset);
+      vector_.insert(vector_.end(), ptr, ptr + num_bytes);
+      return new_offset;
+    }
+
+    const std::vector<uint8_t>& Data() const { return vector_; }
+
+   private:
+    struct IdentityHash {
+      size_t operator()(uint32_t v) const { return v; }
+    };
+
+    // We store the full hash as the key to simplify growing of the table.
+    // It avoids storing or referencing the actual data in the hash-table.
+    std::unordered_multimap<uint32_t, size_t, IdentityHash> hash_to_offset_;
+
+    std::vector<uint8_t> vector_;
+  };
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DEDUP_VECTOR_H_
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index 6bb22ed..e9cd421 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -285,7 +285,7 @@
   constexpr bool is64bit = false;
   DebugInfoEntryWriter<> info(is64bit, &debug_abbrev_data_);
   DW_CHECK("Contents of the .debug_info section:");
-  info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes);
+  info.StartTag(dwarf::DW_TAG_compile_unit);
   DW_CHECK("Abbrev Number: 1 (DW_TAG_compile_unit)");
   info.WriteStrp(dwarf::DW_AT_producer, "Compiler name", &debug_str_data_);
   DW_CHECK_NEXT("DW_AT_producer    : (indirect string, offset: 0x0): Compiler name");
@@ -293,7 +293,7 @@
   DW_CHECK_NEXT("DW_AT_low_pc      : 0x1000000");
   info.WriteAddr(dwarf::DW_AT_high_pc, 0x02000000);
   DW_CHECK_NEXT("DW_AT_high_pc     : 0x2000000");
-  info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no);
+  info.StartTag(dwarf::DW_TAG_subprogram);
   DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)");
   info.WriteStrp(dwarf::DW_AT_name, "Foo", &debug_str_data_);
   DW_CHECK_NEXT("DW_AT_name        : (indirect string, offset: 0xe): Foo");
@@ -302,7 +302,7 @@
   info.WriteAddr(dwarf::DW_AT_high_pc, 0x01020000);
   DW_CHECK_NEXT("DW_AT_high_pc     : 0x1020000");
   info.EndTag();  // DW_TAG_subprogram
-  info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no);
+  info.StartTag(dwarf::DW_TAG_subprogram);
   DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)");
   info.WriteStrp(dwarf::DW_AT_name, "Bar", &debug_str_data_);
   DW_CHECK_NEXT("DW_AT_name        : (indirect string, offset: 0x12): Bar");
@@ -313,7 +313,7 @@
   info.EndTag();  // DW_TAG_subprogram
   info.EndTag();  // DW_TAG_compile_unit
   // Test that previous list was properly terminated and empty children.
-  info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes);
+  info.StartTag(dwarf::DW_TAG_compile_unit);
   info.EndTag();  // DW_TAG_compile_unit
 
   // The abbrev table is just side product, but check it as well.
@@ -327,7 +327,7 @@
   DW_CHECK_NEXT("DW_AT_name         DW_FORM_strp");
   DW_CHECK_NEXT("DW_AT_low_pc       DW_FORM_addr");
   DW_CHECK_NEXT("DW_AT_high_pc      DW_FORM_addr");
-  DW_CHECK("3      DW_TAG_compile_unit    [has children]");
+  DW_CHECK("3      DW_TAG_compile_unit    [no children]");
 
   std::vector<uintptr_t> debug_info_patches;
   std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 };  // NOLINT
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index 633e2f7..c75aeac 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -138,6 +138,7 @@
   writer.PushUint32(debug_abbrev_offset);
   writer.PushUint8(entries.Is64bit() ? 8 : 4);
   size_t entries_offset = writer.data()->size();
+  DCHECK_EQ(entries_offset, DebugInfoEntryWriter<Vector>::kCompilationUnitHeaderSize);
   writer.PushData(*entries.data());
   writer.UpdateUint32(start, writer.data()->size() - start - 4);
   // Copy patch locations and make them relative to .debug_info section.
diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h
index 00b9dfa..d2add7f 100644
--- a/compiler/dwarf/writer.h
+++ b/compiler/dwarf/writer.h
@@ -114,9 +114,9 @@
     data_->insert(data_->end(), value, value + strlen(value) + 1);
   }
 
-  void PushData(const void* ptr, size_t size) {
+  void PushData(const void* ptr, size_t num_bytes) {
     const char* p = reinterpret_cast<const char*>(ptr);
-    data_->insert(data_->end(), p, p + size);
+    data_->insert(data_->end(), p, p + num_bytes);
   }
 
   template<typename Vector2>
@@ -164,6 +164,10 @@
     return data_;
   }
 
+  size_t size() const {
+    return data_->size();
+  }
+
   explicit Writer(Vector* buffer) : data_(buffer) { }
 
  private:
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index e1ab340..5e2a8bf 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -19,9 +19,11 @@
 #include <unordered_set>
 
 #include "base/casts.h"
+#include "base/stl_util.h"
 #include "compiled_method.h"
 #include "driver/compiler_driver.h"
 #include "dex_file-inl.h"
+#include "dwarf/dedup_vector.h"
 #include "dwarf/headers.h"
 #include "dwarf/register.h"
 #include "elf_builder.h"
@@ -249,10 +251,217 @@
   }
 }
 
+struct CompilationUnit {
+  std::vector<const OatWriter::DebugInfo*> methods_;
+  size_t debug_line_offset_ = 0;
+  uint32_t low_pc_ = 0xFFFFFFFFU;
+  uint32_t high_pc_ = 0;
+};
+
+// Helper class to write .debug_info and its supporting sections.
 template<typename ElfTypes>
 class DebugInfoWriter {
   typedef typename ElfTypes::Addr Elf_Addr;
 
+  // Helper class to write one compilation unit.
+  // It holds helper methods and temporary state.
+  class CompilationUnitWriter {
+   public:
+    explicit CompilationUnitWriter(DebugInfoWriter* owner)
+      : owner_(owner),
+        info_(Is64BitInstructionSet(owner_->builder_->GetIsa()), &debug_abbrev_) {
+    }
+
+    void Write(const CompilationUnit& compilation_unit) {
+      CHECK(!compilation_unit.methods_.empty());
+      const Elf_Addr text_address = owner_->builder_->GetText()->GetAddress();
+
+      info_.StartTag(DW_TAG_compile_unit);
+      info_.WriteStrp(DW_AT_producer, owner_->WriteString("Android dex2oat"));
+      info_.WriteData1(DW_AT_language, DW_LANG_Java);
+      info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_);
+      info_.WriteAddr(DW_AT_high_pc, text_address + compilation_unit.high_pc_);
+      info_.WriteData4(DW_AT_stmt_list, compilation_unit.debug_line_offset_);
+
+      const char* last_dex_class_desc = nullptr;
+      for (auto mi : compilation_unit.methods_) {
+        const DexFile* dex = mi->dex_file_;
+        const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index_);
+        const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
+        const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
+        const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
+
+        // Enclose the method in correct class definition.
+        if (last_dex_class_desc != dex_class_desc) {
+          if (last_dex_class_desc != nullptr) {
+            EndClassTag(last_dex_class_desc);
+          }
+          size_t offset = StartClassTag(dex_class_desc);
+          type_cache_.emplace(dex_class_desc, offset);
+          // Check that each class is defined only once.
+          bool unique = owner_->defined_dex_classes_.insert(dex_class_desc).second;
+          CHECK(unique) << "Redefinition of " << dex_class_desc;
+          last_dex_class_desc = dex_class_desc;
+        }
+
+        std::vector<const char*> param_names;
+        if (mi->code_item_ != nullptr) {
+          const uint8_t* stream = dex->GetDebugInfoStream(mi->code_item_);
+          if (stream != nullptr) {
+            DecodeUnsignedLeb128(&stream);  // line.
+            uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+            for (uint32_t i = 0; i < parameters_size; ++i) {
+              uint32_t id = DecodeUnsignedLeb128P1(&stream);
+              param_names.push_back(mi->dex_file_->StringDataByIdx(id));
+            }
+          }
+        }
+
+        int start_depth = info_.Depth();
+        info_.StartTag(DW_TAG_subprogram);
+        WriteName(dex->GetMethodName(dex_method));
+        info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc_);
+        info_.WriteAddr(DW_AT_high_pc, text_address + mi->high_pc_);
+        WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
+        if (dex_params != nullptr) {
+          for (uint32_t i = 0; i < dex_params->Size(); ++i) {
+            info_.StartTag(DW_TAG_formal_parameter);
+            // Parameter names may not be always available.
+            if (i < param_names.size() && param_names[i] != nullptr) {
+              WriteName(param_names[i]);
+            }
+            WriteLazyType(dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_));
+            info_.EndTag();
+          }
+        }
+        info_.EndTag();
+        CHECK_EQ(info_.Depth(), start_depth);  // Balanced start/end.
+      }
+      if (last_dex_class_desc != nullptr) {
+        EndClassTag(last_dex_class_desc);
+      }
+      CHECK_EQ(info_.Depth(), 1);
+      FinishLazyTypes();
+      info_.EndTag();  // DW_TAG_compile_unit
+      std::vector<uint8_t> buffer;
+      buffer.reserve(info_.data()->size() + KB);
+      const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+      const size_t debug_abbrev_offset =
+          owner_->debug_abbrev_.Insert(debug_abbrev_.data(), debug_abbrev_.size());
+      WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+      owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+    }
+
+    // Some types are difficult to define as we go since they need
+    // to be enclosed in the right set of namespaces. Therefore we
+    // just define all types lazily at the end of compilation unit.
+    void WriteLazyType(const char* type_descriptor) {
+      DCHECK(type_descriptor != nullptr);
+      if (type_descriptor[0] != 'V') {
+        lazy_types_.emplace(type_descriptor, info_.size());
+        info_.WriteRef4(DW_AT_type, 0);
+      }
+    }
+
+    void FinishLazyTypes() {
+      for (const auto& lazy_type : lazy_types_) {
+        info_.UpdateUint32(lazy_type.second, WriteType(lazy_type.first));
+      }
+      lazy_types_.clear();
+    }
+
+   private:
+    void WriteName(const char* name) {
+      info_.WriteStrp(DW_AT_name, owner_->WriteString(name));
+    }
+
+    // Convert dex type descriptor to DWARF.
+    // Returns offset in the compilation unit.
+    size_t WriteType(const char* desc) {
+      const auto& it = type_cache_.find(desc);
+      if (it != type_cache_.end()) {
+        return it->second;
+      }
+
+      size_t offset;
+      if (*desc == 'L') {
+        // Class type. For example: Lpackage/name;
+        offset = StartClassTag(desc);
+        info_.WriteFlag(DW_AT_declaration, true);
+        EndClassTag(desc);
+      } else if (*desc == '[') {
+        // Array type.
+        size_t element_type = WriteType(desc + 1);
+        offset = info_.StartTag(DW_TAG_array_type);
+        info_.WriteRef(DW_AT_type, element_type);
+        info_.EndTag();
+      } else {
+        // Primitive types.
+        const char* name;
+        switch (*desc) {
+        case 'B': name = "byte"; break;
+        case 'C': name = "char"; break;
+        case 'D': name = "double"; break;
+        case 'F': name = "float"; break;
+        case 'I': name = "int"; break;
+        case 'J': name = "long"; break;
+        case 'S': name = "short"; break;
+        case 'Z': name = "boolean"; break;
+        case 'V': name = "void"; break;
+        default:
+          LOG(FATAL) << "Unknown dex type descriptor: " << desc;
+          UNREACHABLE();
+        }
+        offset = info_.StartTag(DW_TAG_base_type);
+        WriteName(name);
+        info_.EndTag();
+      }
+
+      type_cache_.emplace(desc, offset);
+      return offset;
+    }
+
+    // Start DW_TAG_class_type tag nested in DW_TAG_namespace tags.
+    // Returns offset of the class tag in the compilation unit.
+    size_t StartClassTag(const char* desc) {
+      DCHECK(desc != nullptr && desc[0] == 'L');
+      // Enclose the type in namespace tags.
+      const char* end;
+      for (desc = desc + 1; (end = strchr(desc, '/')) != nullptr; desc = end + 1) {
+        info_.StartTag(DW_TAG_namespace);
+        WriteName(std::string(desc, end - desc).c_str());
+      }
+      // Start the class tag.
+      size_t offset = info_.StartTag(DW_TAG_class_type);
+      end = strchr(desc, ';');
+      CHECK(end != nullptr);
+      WriteName(std::string(desc, end - desc).c_str());
+      return offset;
+    }
+
+    void EndClassTag(const char* desc) {
+      DCHECK(desc != nullptr && desc[0] == 'L');
+      // End the class tag.
+      info_.EndTag();
+      // Close namespace tags.
+      const char* end;
+      for (desc = desc + 1; (end = strchr(desc, '/')) != nullptr; desc = end + 1) {
+        info_.EndTag();
+      }
+    }
+
+    // For access to the ELF sections.
+    DebugInfoWriter<ElfTypes>* owner_;
+    // Debug abbrevs for this compilation unit only.
+    std::vector<uint8_t> debug_abbrev_;
+    // Temporary buffer to create and store the entries.
+    DebugInfoEntryWriter<> info_;
+    // Cache of already translated type descriptors.
+    std::map<const char*, size_t, CStringLess> type_cache_;  // type_desc -> definition_offset.
+    // 32-bit references which need to be resolved to a type later.
+    std::multimap<const char*, size_t, CStringLess> lazy_types_;  // type_desc -> patch_offset.
+  };
+
  public:
   explicit DebugInfoWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
   }
@@ -261,54 +470,29 @@
     builder_->GetDebugInfo()->Start();
   }
 
-  void Write(const std::vector<const OatWriter::DebugInfo*>& method_infos,
-             size_t debug_line_offset) {
-    const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
-    const Elf_Addr text_address = builder_->GetText()->GetAddress();
-    uint32_t cunit_low_pc = 0xFFFFFFFFU;
-    uint32_t cunit_high_pc = 0;
-    for (auto method_info : method_infos) {
-      cunit_low_pc = std::min(cunit_low_pc, method_info->low_pc_);
-      cunit_high_pc = std::max(cunit_high_pc, method_info->high_pc_);
-    }
-
-    size_t debug_abbrev_offset = debug_abbrev_.size();
-    DebugInfoEntryWriter<> info(is64bit, &debug_abbrev_);
-    info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
-    info.WriteStrp(DW_AT_producer, "Android dex2oat", &debug_str_);
-    info.WriteData1(DW_AT_language, DW_LANG_Java);
-    info.WriteAddr(DW_AT_low_pc, text_address + cunit_low_pc);
-    info.WriteAddr(DW_AT_high_pc, text_address + cunit_high_pc);
-    info.WriteData4(DW_AT_stmt_list, debug_line_offset);
-    for (auto method_info : method_infos) {
-      std::string method_name = PrettyMethod(method_info->dex_method_index_,
-                                             *method_info->dex_file_, true);
-      info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
-      info.WriteStrp(DW_AT_name, method_name.data(), &debug_str_);
-      info.WriteAddr(DW_AT_low_pc, text_address + method_info->low_pc_);
-      info.WriteAddr(DW_AT_high_pc, text_address + method_info->high_pc_);
-      info.EndTag();  // DW_TAG_subprogram
-    }
-    info.EndTag();  // DW_TAG_compile_unit
-    std::vector<uint8_t> buffer;
-    buffer.reserve(info.data()->size() + KB);
-    size_t offset = builder_->GetDebugInfo()->GetSize();
-    WriteDebugInfoCU(debug_abbrev_offset, info, offset, &buffer, &debug_info_patches_);
-    builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+  void WriteCompilationUnit(const CompilationUnit& compilation_unit) {
+    CompilationUnitWriter writer(this);
+    writer.Write(compilation_unit);
   }
 
   void End() {
     builder_->GetDebugInfo()->End();
     builder_->WritePatches(".debug_info.oat_patches", &debug_info_patches_);
-    builder_->WriteSection(".debug_abbrev", &debug_abbrev_);
-    builder_->WriteSection(".debug_str", &debug_str_);
+    builder_->WriteSection(".debug_abbrev", &debug_abbrev_.Data());
+    builder_->WriteSection(".debug_str", &debug_str_.Data());
   }
 
  private:
+  size_t WriteString(const char* str) {
+    return debug_str_.Insert(reinterpret_cast<const uint8_t*>(str), strlen(str) + 1);
+  }
+
   ElfBuilder<ElfTypes>* builder_;
   std::vector<uintptr_t> debug_info_patches_;
-  std::vector<uint8_t> debug_abbrev_;
-  std::vector<uint8_t> debug_str_;
+  DedupVector debug_abbrev_;
+  DedupVector debug_str_;
+
+  std::unordered_set<const char*> defined_dex_classes_;  // For CHECKs only.
 };
 
 template<typename ElfTypes>
@@ -325,15 +509,11 @@
 
   // Write line table for given set of methods.
   // Returns the number of bytes written.
-  size_t Write(const std::vector<const OatWriter::DebugInfo*>& method_infos) {
+  size_t WriteCompilationUnit(CompilationUnit& compilation_unit) {
     const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
     const Elf_Addr text_address = builder_->GetText()->GetAddress();
-    uint32_t cunit_low_pc = 0xFFFFFFFFU;
-    uint32_t cunit_high_pc = 0;
-    for (auto method_info : method_infos) {
-      cunit_low_pc = std::min(cunit_low_pc, method_info->low_pc_);
-      cunit_high_pc = std::max(cunit_high_pc, method_info->high_pc_);
-    }
+
+    compilation_unit.debug_line_offset_ = builder_->GetDebugLine()->GetSize();
 
     std::vector<FileEntry> files;
     std::unordered_map<std::string, size_t> files_map;
@@ -358,11 +538,17 @@
         break;
     }
     DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
-    opcodes.SetAddress(text_address + cunit_low_pc);
+    opcodes.SetAddress(text_address + compilation_unit.low_pc_);
     if (dwarf_isa != -1) {
       opcodes.SetISA(dwarf_isa);
     }
-    for (const OatWriter::DebugInfo* mi : method_infos) {
+    for (const OatWriter::DebugInfo* mi : compilation_unit.methods_) {
+      // Ignore function if we have already generated line table for the same address.
+      // It would confuse the debugger and the DWARF specification forbids it.
+      if (mi->deduped_) {
+        continue;
+      }
+
       struct DebugInfoCallbacks {
         static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
           auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
@@ -461,7 +647,7 @@
         opcodes.AddRow(method_address, 0);
       }
     }
-    opcodes.AdvancePC(text_address + cunit_high_pc);
+    opcodes.AdvancePC(text_address + compilation_unit.high_pc_);
     opcodes.EndSequence();
     std::vector<uint8_t> buffer;
     buffer.reserve(opcodes.data()->size() + KB);
@@ -484,36 +670,28 @@
 template<typename ElfTypes>
 void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
                         const std::vector<OatWriter::DebugInfo>& method_infos) {
-  struct CompilationUnit {
-    std::vector<const OatWriter::DebugInfo*> methods_;
-    size_t debug_line_offset_ = 0;
-  };
-
   // Group the methods into compilation units based on source file.
   std::vector<CompilationUnit> compilation_units;
   const char* last_source_file = nullptr;
   for (const OatWriter::DebugInfo& mi : method_infos) {
-    // Attribute given instruction range only to single method.
-    // Otherwise the debugger might get really confused.
-    if (!mi.deduped_) {
-      auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
-      const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
-      if (compilation_units.empty() || source_file != last_source_file) {
-        compilation_units.push_back(CompilationUnit());
-      }
-      compilation_units.back().methods_.push_back(&mi);
-      last_source_file = source_file;
+    auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
+    const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
+    if (compilation_units.empty() || source_file != last_source_file) {
+      compilation_units.push_back(CompilationUnit());
     }
+    CompilationUnit& cu = compilation_units.back();
+    cu.methods_.push_back(&mi);
+    cu.low_pc_ = std::min(cu.low_pc_, mi.low_pc_);
+    cu.high_pc_ = std::max(cu.high_pc_, mi.high_pc_);
+    last_source_file = source_file;
   }
 
   // Write .debug_line section.
   {
     DebugLineWriter<ElfTypes> line_writer(builder);
     line_writer.Start();
-    size_t offset = 0;
     for (auto& compilation_unit : compilation_units) {
-      compilation_unit.debug_line_offset_ = offset;
-      offset += line_writer.Write(compilation_unit.methods_);
+      line_writer.WriteCompilationUnit(compilation_unit);
     }
     line_writer.End();
   }
@@ -523,7 +701,7 @@
     DebugInfoWriter<ElfTypes> info_writer(builder);
     info_writer.Start();
     for (const auto& compilation_unit : compilation_units) {
-      info_writer.Write(compilation_unit.methods_, compilation_unit.debug_line_offset_);
+      info_writer.WriteCompilationUnit(compilation_unit);
     }
     info_writer.End();
   }
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 3257de1..590c3ad 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -876,12 +876,78 @@
                       clinit_check);
 }
 
+bool HGraphBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
+  bool finalizable;
+  bool can_throw = NeedsAccessCheck(type_index, &finalizable);
+
+  // Only the non-resolved entrypoint handles the finalizable class case. If we
+  // need access checks, then we haven't resolved the method and the class may
+  // again be finalizable.
+  QuickEntrypointEnum entrypoint = (finalizable || can_throw)
+      ? kQuickAllocObject
+      : kQuickAllocObjectInitialized;
+
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+      dex_compilation_unit_->GetClassLinker()->FindDexCache(
+          soa.Self(), *dex_compilation_unit_->GetDexFile())));
+  Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+  const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+  Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+      outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
+
+  if (outer_dex_cache.Get() != dex_cache.Get()) {
+    // We currently do not support inlining allocations across dex files.
+    return false;
+  }
+
+  HLoadClass* load_class = new (arena_) HLoadClass(
+      graph_->GetCurrentMethod(),
+      type_index,
+      *dex_compilation_unit_->GetDexFile(),
+      IsOutermostCompilingClass(type_index),
+      dex_pc,
+      /*needs_access_check*/ can_throw);
+
+  current_block_->AddInstruction(load_class);
+  HInstruction* cls = load_class;
+  if (!IsInitialized(resolved_class, type_index)) {
+    cls = new (arena_) HClinitCheck(load_class, dex_pc);
+    current_block_->AddInstruction(cls);
+  }
+
+  current_block_->AddInstruction(new (arena_) HNewInstance(
+      cls,
+      graph_->GetCurrentMethod(),
+      dex_pc,
+      type_index,
+      *dex_compilation_unit_->GetDexFile(),
+      can_throw,
+      finalizable,
+      entrypoint));
+  return true;
+}
+
+bool HGraphBuilder::IsInitialized(Handle<mirror::Class> cls, uint16_t type_index) const {
+  if (cls.Get() == nullptr) {
+    return false;
+  }
+  if (GetOutermostCompilingClass() == cls.Get()) {
+    return true;
+  }
+  // TODO: find out why this check is needed.
+  bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
+      *outer_compilation_unit_->GetDexFile(), type_index);
+  return cls->IsInitialized() && is_in_dex_cache;
+}
+
 HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke(
       uint32_t dex_pc,
       uint32_t method_idx,
       HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<4> hs(soa.Self());
+  StackHandleScope<5> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(
       dex_compilation_unit_->GetClassLinker()->FindDexCache(
           soa.Self(), *dex_compilation_unit_->GetDexFile())));
@@ -927,13 +993,8 @@
     // whether we should add an explicit class initialization
     // check for its declaring class before the static method call.
 
-    // TODO: find out why this check is needed.
-    bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
-        *outer_compilation_unit_->GetDexFile(), storage_index);
-    bool is_initialized =
-        resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
-
-    if (is_initialized) {
+    Handle<mirror::Class> cls(hs.NewHandle(resolved_method->GetDeclaringClass()));
+    if (IsInitialized(cls, storage_index)) {
       *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
     } else {
       *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
@@ -1272,7 +1333,7 @@
   uint16_t field_index = instruction.VRegB_21c();
 
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<4> hs(soa.Self());
+  StackHandleScope<5> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(
       dex_compilation_unit_->GetClassLinker()->FindDexCache(
           soa.Self(), *dex_compilation_unit_->GetDexFile())));
@@ -1318,11 +1379,6 @@
     }
   }
 
-  // TODO: find out why this check is needed.
-  bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
-      *outer_compilation_unit_->GetDexFile(), storage_index);
-  bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
-
   HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
                                                  storage_index,
                                                  *dex_compilation_unit_->GetDexFile(),
@@ -1332,12 +1388,14 @@
   current_block_->AddInstruction(constant);
 
   HInstruction* cls = constant;
-  if (!is_initialized && !is_outer_class) {
+
+  Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass()));
+  if (!IsInitialized(klass, storage_index)){
     cls = new (arena_) HClinitCheck(constant, dex_pc);
     current_block_->AddInstruction(cls);
   }
 
-  uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
+  uint16_t class_def_index = klass->GetDexClassDefIndex();
   if (is_put) {
     // We need to keep the class alive before loading the value.
     Temporaries temps(graph_);
@@ -2509,20 +2567,9 @@
         current_block_->AddInstruction(fake_string);
         UpdateLocal(register_index, fake_string, dex_pc);
       } else {
-        bool finalizable;
-        bool can_throw = NeedsAccessCheck(type_index, &finalizable);
-        QuickEntrypointEnum entrypoint = can_throw
-            ? kQuickAllocObjectWithAccessCheck
-            : kQuickAllocObject;
-
-        current_block_->AddInstruction(new (arena_) HNewInstance(
-            graph_->GetCurrentMethod(),
-            dex_pc,
-            type_index,
-            *dex_compilation_unit_->GetDexFile(),
-            can_throw,
-            finalizable,
-            entrypoint));
+        if (!BuildNewInstance(type_index, dex_pc)) {
+          return false;
+        }
         UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
       }
       break;
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index f857ef0..615b0cd 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -308,6 +308,14 @@
       uint32_t method_idx,
       HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement);
 
+  // Build a HNewInstance instruction.
+  bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc);
+
+  // Return whether the compiler can assume `cls` is initialized. `type_index` is the index
+  // of the class in the outer dex file.
+  bool IsInitialized(Handle<mirror::Class> cls, uint16_t type_index) const
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   ArenaAllocator* const arena_;
 
   // A list of the size of the dex code holding block information for
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cb6bed0..1c3bd6c 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3413,14 +3413,12 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   locations->SetOut(Location::RegisterLocation(R0));
 }
 
 void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(),
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 2776b7d..04955dd 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3372,17 +3372,13 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
   CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
 }
 
 void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
-  DCHECK(type_index.Is(w0));
-  __ Mov(type_index, instruction->GetTypeIndex());
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(),
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 801e203..f3178bd 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -3478,17 +3478,12 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
 }
 
 void InstructionCodeGeneratorMIPS::VisitNewInstance(HNewInstance* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  Register current_method_register = calling_convention.GetRegisterAt(1);
-  __ Lw(current_method_register, SP, kCurrentMethodStackOffset);
-  // Move an uint16_t value to a register.
-  __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
   codegen_->InvokeRuntime(
       GetThreadOffset<kMipsWordSize>(instruction->GetEntrypoint()).Int32Value(),
       instruction,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 7b33075..6100859 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3266,15 +3266,12 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
 }
 
 void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  // Move an uint16_t value to a register.
-  __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex());
   codegen_->InvokeRuntime(instruction->GetEntrypoint(),
                           instruction,
                           instruction->GetDexPc(),
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a87e8ed..53e33bf 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3769,13 +3769,11 @@
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   locations->SetOut(Location::RegisterLocation(EAX));
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(),
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index dcc1808..0e0b869 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3765,18 +3765,14 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
   locations->SetOut(Location::RegisterLocation(RAX));
 }
 
 void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
-  InvokeRuntimeCallingConvention calling_convention;
-  codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)),
-                           instruction->GetTypeIndex());
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
-
   codegen_->InvokeRuntime(instruction->GetEntrypoint(),
                           instruction,
                           instruction->GetDexPc(),
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index c36de84..4af111b 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -377,9 +377,10 @@
 
   HInstruction* current = block->GetFirstInstruction();
   while (current != nullptr) {
-    set->Kill(current->GetSideEffects());
     // Save the next instruction in case `current` is removed from the graph.
     HInstruction* next = current->GetNext();
+    // Do not kill the set with the side effects of the instruction just now: if
+    // the instruction is GVN'ed, we don't need to kill.
     if (current->CanBeMoved()) {
       if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
         // For commutative ops, (x op y) will be treated the same as (y op x)
@@ -395,8 +396,11 @@
         current->ReplaceWith(existing);
         current->GetBlock()->RemoveInstruction(current);
       } else {
+        set->Kill(current->GetSideEffects());
         set->Add(current);
       }
+    } else {
+      set->Kill(current->GetSideEffects());
     }
     current = next;
   }
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index a94e3a8..3268445 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -138,6 +138,221 @@
 
 #define __ assembler->
 
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
+  FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
+
+  if (is64bit) {
+    Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+    Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+    __ Mfc1(out_lo, in);
+    __ Mfhc1(out_hi, in);
+  } else {
+    Register out = locations->Out().AsRegister<Register>();
+
+    __ Mfc1(out, in);
+  }
+}
+
+// long java.lang.Double.doubleToRawLongBits(double)
+void IntrinsicLocationsBuilderMIPS::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
+}
+
+// int java.lang.Float.floatToRawIntBits(float)
+void IntrinsicLocationsBuilderMIPS::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
+  FRegister out = locations->Out().AsFpuRegister<FRegister>();
+
+  if (is64bit) {
+    Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+    Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+
+    __ Mtc1(in_lo, out);
+    __ Mthc1(in_hi, out);
+  } else {
+    Register in = locations->InAt(0).AsRegister<Register>();
+
+    __ Mtc1(in, out);
+  }
+}
+
+// double java.lang.Double.longBitsToDouble(long)
+void IntrinsicLocationsBuilderMIPS::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+// float java.lang.Float.intBitsToFloat(int)
+void IntrinsicLocationsBuilderMIPS::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenReverseBytes(LocationSummary* locations,
+                            Primitive::Type type,
+                            MipsAssembler* assembler,
+                            bool isR2OrNewer) {
+  DCHECK(type == Primitive::kPrimShort ||
+         type == Primitive::kPrimInt ||
+         type == Primitive::kPrimLong);
+
+  if (type == Primitive::kPrimShort) {
+    Register in = locations->InAt(0).AsRegister<Register>();
+    Register out = locations->Out().AsRegister<Register>();
+
+    if (isR2OrNewer) {
+      __ Wsbh(out, in);
+      __ Seh(out, out);
+    } else {
+      __ Sll(TMP, in, 24);
+      __ Sra(TMP, TMP, 16);
+      __ Sll(out, in, 16);
+      __ Srl(out, out, 24);
+      __ Or(out, out, TMP);
+    }
+  } else if (type == Primitive::kPrimInt) {
+    Register in = locations->InAt(0).AsRegister<Register>();
+    Register out = locations->Out().AsRegister<Register>();
+
+    if (isR2OrNewer) {
+      __ Rotr(out, in, 16);
+      __ Wsbh(out, out);
+    } else {
+      // MIPS32r1
+      // __ Rotr(out, in, 16);
+      __ Sll(TMP, in, 16);
+      __ Srl(out, in, 16);
+      __ Or(out, out, TMP);
+      // __ Wsbh(out, out);
+      __ LoadConst32(AT, 0x00FF00FF);
+      __ And(TMP, out, AT);
+      __ Sll(TMP, TMP, 8);
+      __ Srl(out, out, 8);
+      __ And(out, out, AT);
+      __ Or(out, out, TMP);
+    }
+  } else if (type == Primitive::kPrimLong) {
+    Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+    Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+    Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+    Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+    if (isR2OrNewer) {
+      __ Rotr(AT, in_hi, 16);
+      __ Rotr(TMP, in_lo, 16);
+      __ Wsbh(out_lo, AT);
+      __ Wsbh(out_hi, TMP);
+    } else {
+      // When calling CreateIntToIntLocations() we promised that the
+      // use of the out_lo/out_hi wouldn't overlap with the use of
+      // in_lo/in_hi. Be very careful not to write to out_lo/out_hi
+      // until we're completely done reading from in_lo/in_hi.
+      // __ Rotr(TMP, in_lo, 16);
+      __ Sll(TMP, in_lo, 16);
+      __ Srl(AT, in_lo, 16);
+      __ Or(TMP, TMP, AT);             // Hold in TMP until it's safe
+                                       // to write to out_hi.
+      // __ Rotr(out_lo, in_hi, 16);
+      __ Sll(AT, in_hi, 16);
+      __ Srl(out_lo, in_hi, 16);        // Here we are finally done reading
+                                        // from in_lo/in_hi so it's okay to
+                                        // write to out_lo/out_hi.
+      __ Or(out_lo, out_lo, AT);
+      // __ Wsbh(out_hi, out_hi);
+      __ LoadConst32(AT, 0x00FF00FF);
+      __ And(out_hi, TMP, AT);
+      __ Sll(out_hi, out_hi, 8);
+      __ Srl(TMP, TMP, 8);
+      __ And(TMP, TMP, AT);
+      __ Or(out_hi, out_hi, TMP);
+      // __ Wsbh(out_lo, out_lo);
+      __ And(TMP, out_lo, AT);  // AT already holds the correct mask value
+      __ Sll(TMP, TMP, 8);
+      __ Srl(out_lo, out_lo, 8);
+      __ And(out_lo, out_lo, AT);
+      __ Or(out_lo, out_lo, TMP);
+    }
+  }
+}
+
+// int java.lang.Integer.reverseBytes(int)
+void IntrinsicLocationsBuilderMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(),
+                  Primitive::kPrimInt,
+                  GetAssembler(),
+                  codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2());
+}
+
+// long java.lang.Long.reverseBytes(long)
+void IntrinsicLocationsBuilderMIPS::VisitLongReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitLongReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(),
+                  Primitive::kPrimLong,
+                  GetAssembler(),
+                  codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2());
+}
+
+// short java.lang.Short.reverseBytes(short)
+void IntrinsicLocationsBuilderMIPS::VisitShortReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitShortReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(),
+                  Primitive::kPrimShort,
+                  GetAssembler(),
+                  codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2());
+}
+
 // boolean java.lang.String.equals(Object anObject)
 void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) {
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -250,15 +465,8 @@
 
 UNIMPLEMENTED_INTRINSIC(IntegerReverse)
 UNIMPLEMENTED_INTRINSIC(LongReverse)
-UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
-UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
-UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
 UNIMPLEMENTED_INTRINSIC(LongNumberOfLeadingZeros)
 UNIMPLEMENTED_INTRINSIC(IntegerNumberOfLeadingZeros)
-UNIMPLEMENTED_INTRINSIC(FloatIntBitsToFloat)
-UNIMPLEMENTED_INTRINSIC(DoubleLongBitsToDouble)
-UNIMPLEMENTED_INTRINSIC(FloatFloatToRawIntBits)
-UNIMPLEMENTED_INTRINSIC(DoubleDoubleToRawLongBits)
 UNIMPLEMENTED_INTRINSIC(MathAbsDouble)
 UNIMPLEMENTED_INTRINSIC(MathAbsFloat)
 UNIMPLEMENTED_INTRINSIC(MathAbsInt)
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 744067f..a5ea154 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3657,9 +3657,10 @@
   DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
 };
 
-class HNewInstance : public HExpression<1> {
+class HNewInstance : public HExpression<2> {
  public:
-  HNewInstance(HCurrentMethod* current_method,
+  HNewInstance(HInstruction* cls,
+               HCurrentMethod* current_method,
                uint32_t dex_pc,
                uint16_t type_index,
                const DexFile& dex_file,
@@ -3672,7 +3673,8 @@
         can_throw_(can_throw),
         finalizable_(finalizable),
         entrypoint_(entrypoint) {
-    SetRawInputAt(0, current_method);
+    SetRawInputAt(0, cls);
+    SetRawInputAt(1, current_method);
   }
 
   uint16_t GetTypeIndex() const { return type_index_; }
@@ -3692,6 +3694,10 @@
 
   QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
 
+  void SetEntrypoint(QuickEntrypointEnum entrypoint) {
+    entrypoint_ = entrypoint;
+  }
+
   DECLARE_INSTRUCTION(NewInstance);
 
  private:
@@ -3699,7 +3705,7 @@
   const DexFile& dex_file_;
   const bool can_throw_;
   const bool finalizable_;
-  const QuickEntrypointEnum entrypoint_;
+  QuickEntrypointEnum entrypoint_;
 
   DISALLOW_COPY_AND_ASSIGN(HNewInstance);
 };
@@ -4932,6 +4938,7 @@
     return true;
   }
 
+  bool CanThrow() const OVERRIDE { return true; }
 
   HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
 
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index f3d075c..dd39658 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -48,22 +48,34 @@
 }
 
 void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
-  // Try to find a static invoke from which this check originated.
-  HInvokeStaticOrDirect* invoke = nullptr;
+  // Try to find a static invoke or a new-instance from which this check originated.
+  HInstruction* implicit_clinit = nullptr;
   for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); it.Advance()) {
     HInstruction* user = it.Current()->GetUser();
-    if (user->IsInvokeStaticOrDirect() && CanMoveClinitCheck(check, user)) {
-      invoke = user->AsInvokeStaticOrDirect();
-      DCHECK(invoke->IsStaticWithExplicitClinitCheck());
-      invoke->RemoveExplicitClinitCheck(HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
+    if ((user->IsInvokeStaticOrDirect() || user->IsNewInstance()) &&
+        CanMoveClinitCheck(check, user)) {
+      implicit_clinit = user;
+      if (user->IsInvokeStaticOrDirect()) {
+        DCHECK(user->AsInvokeStaticOrDirect()->IsStaticWithExplicitClinitCheck());
+        user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
+            HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
+      } else {
+        DCHECK(user->IsNewInstance());
+        // We delegate the initialization duty to the allocation.
+        if (user->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectInitialized) {
+          user->AsNewInstance()->SetEntrypoint(kQuickAllocObjectResolved);
+        }
+      }
       break;
     }
   }
-  // If we found a static invoke for merging, remove the check from all other static invokes.
-  if (invoke != nullptr) {
+  // If we found a static invoke or new-instance for merging, remove the check
+  // from dominated static invokes.
+  if (implicit_clinit != nullptr) {
     for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); ) {
       HInstruction* user = it.Current()->GetUser();
-      DCHECK(invoke->StrictlyDominates(user));  // All other uses must be dominated.
+      // All other uses must be dominated.
+      DCHECK(implicit_clinit->StrictlyDominates(user) || (implicit_clinit == user));
       it.Advance();  // Advance before we remove the node, reference to the next node is preserved.
       if (user->IsInvokeStaticOrDirect()) {
         user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
@@ -77,8 +89,8 @@
 
   check->ReplaceWith(load_class);
 
-  if (invoke != nullptr) {
-    // Remove the check from the graph. It has been merged into the invoke.
+  if (implicit_clinit != nullptr) {
+    // Remove the check from the graph. It has been merged into the invoke or new-instance.
     check->GetBlock()->RemoveInstruction(check);
     // Check if we can merge the load class as well.
     if (can_merge_with_load_class && !load_class->HasUses()) {
@@ -92,6 +104,27 @@
   }
 }
 
+void PrepareForRegisterAllocation::VisitNewInstance(HNewInstance* instruction) {
+  HLoadClass* load_class = instruction->InputAt(0)->AsLoadClass();
+  bool has_only_one_use = load_class->HasOnlyOneNonEnvironmentUse();
+  // Change the entrypoint to kQuickAllocObject if either:
+  // - the class is finalizable (only kQuickAllocObject handles finalizable classes),
+  // - the class needs access checks (we do not know if it's finalizable),
+  // - or the load class has only one use.
+  if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) {
+    instruction->SetEntrypoint(kQuickAllocObject);
+    instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0);
+    if (has_only_one_use) {
+      // We can remove the load class from the graph. If it needed access checks, we delegate
+      // the access check to the allocation.
+      if (load_class->NeedsAccessCheck()) {
+        instruction->SetEntrypoint(kQuickAllocObjectWithAccessCheck);
+      }
+      load_class->GetBlock()->RemoveInstruction(load_class);
+    }
+  }
+}
+
 void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
   bool needs_materialization = false;
   if (!condition->GetUses().HasOnlyOneUse() || !condition->GetEnvUses().IsEmpty()) {
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index a70fb30..9b24342 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,6 +40,7 @@
   void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
   void VisitCondition(HCondition* condition) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
+  void VisitNewInstance(HNewInstance* instruction) OVERRIDE;
 
   bool CanMoveClinitCheck(HInstruction* input, HInstruction* user);
 
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index aee6412..fc7ac70 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -310,15 +310,27 @@
   EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20);
 }
 
+void MipsAssembler::Wsbh(Register rd, Register rt) {
+  EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20);
+}
+
 void MipsAssembler::Sll(Register rd, Register rt, int shamt) {
+  CHECK(IsUint<5>(shamt)) << shamt;
   EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00);
 }
 
 void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
+  CHECK(IsUint<5>(shamt)) << shamt;
   EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02);
 }
 
+void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
+  CHECK(IsUint<5>(shamt)) << shamt;
+  EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02);
+}
+
 void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
+  CHECK(IsUint<5>(shamt)) << shamt;
   EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03);
 }
 
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 4038c1f..1ef0992 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -135,9 +135,11 @@
 
   void Seb(Register rd, Register rt);  // R2+
   void Seh(Register rd, Register rt);  // R2+
+  void Wsbh(Register rd, Register rt);  // R2+
 
   void Sll(Register rd, Register rt, int shamt);
   void Srl(Register rd, Register rt, int shamt);
+  void Rotr(Register rd, Register rt, int shamt);  // R2+
   void Sra(Register rd, Register rt, int shamt);
   void Sllv(Register rd, Register rt, Register rs);
   void Srlv(Register rd, Register rt, Register rs);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 0b0f094..4f4792a 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -106,6 +106,7 @@
   jit/profiling_info.cc \
   lambda/art_lambda_method.cc \
   lambda/box_table.cc \
+  lambda/box_class_table.cc \
   lambda/closure.cc \
   lambda/closure_builder.cc \
   lambda/leaking_allocator.cc \
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index d6ba304..771c8b7 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -46,9 +46,15 @@
   }
 };
 
+}  // namespace art
+
 // Common tests are declared next to the constants.
 #define ADD_TEST_EQ(x, y) EXPECT_EQ(x, y);
 #include "asm_support.h"
+// Important: Do not include this inside of another namespace, since asm_support.h
+// defines its own namespace which must not be nested.
+
+namespace art {
 
 TEST_F(ArchTest, CheckCommonOffsetsAndSizes) {
   CheckAsmSupportOffsetsAndSizes();
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 631b784..588268d 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1045,6 +1045,26 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
 
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+     .extern artQuickLambdaProxyInvokeHandler
+ENTRY art_quick_lambda_proxy_invoke_handler
+// TODO: have a faster handler that doesn't need to set up a frame
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0
+    mov     r2, r9                 @ pass Thread::Current
+    mov     r3, sp                 @ pass SP
+    blx     artQuickLambdaProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
+    ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
+    // Tear down the callee-save frame. Skip arg registers.
+    add     sp, #(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    .cfi_adjust_cfa_offset -(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    cbnz    r2, 1f                 @ success if no exception is pending
+    vmov    d0, r0, r1             @ store into fpr, for when it's a fpr return...
+    bx      lr                     @ return on success
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_lambda_proxy_invoke_handler
+
     /*
      * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
      * dex method index.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 9ccabad..177873d 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1582,6 +1582,28 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
 
+     /*
+     * Called by managed code that is attempting to call a method on a lambda proxy class. On entry
+     * x0 holds the lambda proxy method and x1 holds the receiver; The frame size of the invoked
+     * lambda proxy method agrees with a ref and args callee save frame.
+     */
+     .extern artQuickLambdaProxyInvokeHandler
+ENTRY art_quick_lambda_proxy_invoke_handler
+// TODO: have a faster way to invoke lambda proxies without setting up the whole frame.
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0
+    mov     x2, xSELF                   // pass Thread::Current
+    mov     x3, sp                      // pass SP
+    bl      artQuickLambdaProxyInvokeHandler  // (Method* proxy method, receiver, Thread*, SP)
+    ldr     x2, [xSELF, THREAD_EXCEPTION_OFFSET]
+    cbnz    x2, .Lexception_in_lambda_proxy    // success if no exception is pending
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame
+    fmov    d0, x0                      // Store result in d0 in case it was float or double
+    ret                                 // return on success
+.Lexception_in_lambda_proxy:
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    DELIVER_PENDING_EXCEPTION
+END art_quick_lambda_proxy_invoke_handler
+
     /*
      * Called to resolve an imt conflict. xIP1 is a hidden argument that holds the target method's
      * dex method index.
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 0691f2a..af79f5e 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1377,6 +1377,10 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
 
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+    .extern artQuickLambdaProxyInvokeHandler
+UNIMPLEMENTED art_quick_lambda_proxy_invoke_handler
+
     /*
      * Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's
      * dex method index.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 66c8aad..5e70a95 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1431,6 +1431,10 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
 
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+    .extern artQuickLambdaProxyInvokeHandler
+UNIMPLEMENTED art_quick_lambda_proxy_invoke_handler
+
     /*
      * Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's
      * dex method index.
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 463c9cf..4fb6119 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1391,6 +1391,149 @@
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_proxy_invoke_handler
 
+#if LAMBDA_INVOKE_USES_LONG
+#undef LAMBDA_PROXY_SETUP_FRAME
+// We need to always do a 'pop' to readjust the stack, so we have to use the slower call instruction.
+#define LAMBDA_PROXY_SETUP_FRAME 1
+#define LAMBDA_INVOKE_REALIGN_STACK_FRAME 1
+#else
+#define LAMBDA_INVOKE_REALIGN_STACK_FRAME 0
+#endif
+
+#define LAMBDA_INVOKE_CALLS_INTO_RUNTIME LAMBDA_INVOKE_REALIGN_STACK_FRAME
+
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+DEFINE_FUNCTION art_quick_lambda_proxy_invoke_handler
+    // This function is always called when the lambda is innate.
+    // Therefore we can assume the box is to an innate lambda.
+    // TODO: perhaps there should be a DCHECK to make sure it's innate?
+
+#if LAMBDA_PROXY_SETUP_FRAME
+    // Set up a quick frame when debugging so we can see that it's going through a stub.
+    // An invoke-virtual + a stub invocation is enough of a hint that we *could* be
+    // going through a lambda proxy.
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX
+#endif
+
+#if !LAMBDA_INVOKE_CALLS_INTO_RUNTIME
+    // Rewrite the following 2 arguments, stored on stack frame:
+    //
+    //   |--------|
+    //   |receiver|   <- esp-4
+    //   |--------|
+    //   | method |   <- esp
+    //   |--------|
+
+    // Set up the new correct method receiver (swap object with closure).
+    // -- The original object is no longer available after this.
+    //
+    // (Before)
+    // ecx == mirror::Object* boxed_lambda;  // lambda proxy object.
+    movl MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET(%ecx), %ecx
+    // (After)
+    // lambda::Closure* closure = boxed_lambda->closure_;
+    // boxed_lambda = closure; // Overwrite lambda proxy object
+    // ecx == closure
+
+    // Look up the new correct method target.
+    // -- The original method target is no longer available after this.
+    //
+    // (Before)
+    // eax == ArtMethod* old_receiver_method;
+    movl LAMBDA_CLOSURE_METHOD_OFFSET(%ecx), %eax
+    // (After)
+    // ArtLambdaMethod* lambda_method_target = closure->lambda_info_;
+    // eax = lambda_method_target
+    //
+    // Set up the correct method target from the lambda info.
+    movl ART_LAMBDA_METHOD_ART_METHOD_OFFSET(%eax), %eax  // Load new receiver method
+    // (After)
+    // ArtMethod* target_method = lambda_method_target->target_
+    // eax = target_method
+#endif
+
+#if LAMBDA_INVOKE_CALLS_INTO_RUNTIME
+    PUSH esp                      // pass SP
+    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    PUSH ecx                      // pass receiver
+    PUSH eax                      // pass proxy method
+    call SYMBOL(artQuickLambdaProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    movd %eax, %xmm0              // place return value also into floating point return value
+    movd %edx, %xmm1
+    punpckldq %xmm1, %xmm0
+    addl LITERAL(16 + FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE), %esp
+    CFI_ADJUST_CFA_OFFSET(-(16 + FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE))
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
+#endif
+
+#if LAMBDA_INVOKE_USES_LONG && !LAMBDA_INVOKE_REALIGN_STACK_FRAME
+    // As a temporary workaround, lambda functions look like
+    //  (J[Arg2][Arg3][Arg4]...)
+    // This means that we can't just pass in the lambda as a 32-bit pointer
+    // We pad the arguments with an extra 32-bit "0" where Arg2 used to be instead.
+
+    // Required arguments for a lambda method:
+    //
+    // Arg0 = eax = method
+    // Arg1 = ecx = closure (hi)
+    // Arg2 = edx = closure (lo)
+    // Arg3 = ebx = <?> (first user-defined argument)
+
+    // Transformation diagram:
+    //
+    // Arg0  Arg1  Arg2  Arg3 ... ArgN
+    //   |    |        \     \        \
+    //   |    |         \     \        \
+    // Arg0  Arg1  0x00  Arg2  Arg3 ... ArgN
+    //              /\
+    //           (inserted)
+    PUSH ebx          // Move out Arg3 into Arg4, and also for all K>3 ArgK into ArgK+1
+    mov %edx, %ebx    // Move out Arg2 into Arg3
+    xor %edx, %edx    // Clear closure 32-bit low register
+
+    // XX: Does this work at all ? This probably breaks the visitors (*and* its unaligned).
+
+    // FIXME: call into the runtime and do a proxy-like-invoke
+    // using a ShadowFrame quick visitor, and then use ArtMethod::Invoke
+    // to call into the actual method (which will take care of fixing up alignment).
+    // Trying to realign in the assembly itself won't actually work
+    // since then the visitor will unwind incorrectly (unless we also fixed up the ManagedStack).
+#endif
+
+    // TODO: avoid extra indirect load by subclass ArtLambdaMethod from ArtMethod.
+
+    // Forward the call to the overwritten receiver method.
+    // -- Arguments [2,N] are left completely untouched since the signature is otherwise identical.
+#if LAMBDA_PROXY_SETUP_FRAME
+  #if LAMBDA_INVOKE_CALLS_INTO_RUNTIME
+    // Have to call into runtime in order to re-align the stack frame to 16 bytes.
+    int3
+  #else
+    // Just call into the method directly. Don't worry about realigning.
+    call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)  // (new method, new receiver, old args...)
+
+    // The stack frame was manually adjusted, so make sure we have a pop here to fix it back.
+    #if LAMBDA_INVOKE_USES_LONG && !LAMBDA_INVOKE_REALIGN_STACK_FRAME
+
+    POP ecx   // OK: ecx is scratch register after the call.
+    // XX: use 'add esp, 4' instead if we need to keep the register? This way we get cleaner CFI.
+    #endif
+  #endif
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+
+#else
+    // Do not use 'call' here since the stack visitors wouldn't know how to visit this frame.
+    jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)   // tailcall (new method, new receiver, old args...)
+#endif
+
+#if LAMBDA_PROXY_SETUP_FRAME
+    ret
+#endif
+
+END_FUNCTION art_quick_lambda_proxy_invoke_handler
+
     /*
      * Called to resolve an imt conflict. xmm7 is a hidden argument that holds the target method's
      * dex method index.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 17d277e..0a54aa3 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1297,7 +1297,6 @@
     RETURN_IF_EAX_ZERO                   // return or deliver exception
 END_FUNCTION art_quick_set64_static
 
-
 DEFINE_FUNCTION art_quick_proxy_invoke_handler
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI
 
@@ -1309,6 +1308,60 @@
     RETURN_OR_DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_proxy_invoke_handler
 
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+DEFINE_FUNCTION art_quick_lambda_proxy_invoke_handler
+    // This function is always called when the lambda is innate.
+    // Therefore we can assume the box is to an innate lambda.
+    // TODO: perhaps there should be a DCHECK to make sure it's innate?
+
+#if LAMBDA_PROXY_SETUP_FRAME
+    // Set up a quick frame when debugging so we can see that it's going through a stub.
+    // Our stack traces will contain the quick lambda proxy hander.
+    // Note that we *must* go through the handler (when spilling) otherwise we won't know how
+    // to move the spilled GC references from the caller to this stub.
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI
+
+    movq %gs:THREAD_SELF_OFFSET, %rdx       // Pass Thread::Current().
+    movq %rsp, %rcx                         // Pass SP.
+    call SYMBOL(artQuickLambdaProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    movq %rax, %xmm0                        // Copy return value in case of float returns.
+    RETURN_OR_DELIVER_PENDING_EXCEPTION
+#else
+    // Set up the new correct method receiver (swap object with closure).
+    // -- The original object is no longer available after this.
+    //
+    // (Before)
+    // rsi == mirror::Object* boxed_lambda;  // lambda proxy object.
+    movq MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET(%rsi), %rsi
+    // (After)
+    // lambda::Closure* closure = boxed_lambda->closure_;  // Overwrite receiver object.
+    // rsi == closure
+
+    // Look up the new correct method target.
+    // -- The original method target is no longer available after this.
+    movq LAMBDA_CLOSURE_METHOD_OFFSET(%rsi), %rdi          // Overwrite old receiver method.
+    // (After)
+    // ArtLambdaMethod* lambda_method_target = closure->lambda_info_;
+    // rdi == lambda_method_target
+
+    // TODO: avoid extra indirect load by subclass ArtLambdaMethod from ArtMethod.
+
+    // Set up the correct method target from the lambda info.
+    movq ART_LAMBDA_METHOD_ART_METHOD_OFFSET(%rdi), %rdi  // Write new receiver method.
+    // (After)
+    // ArtMethod* method_target = lambda_method_target->target_;
+    // rdi == method_target
+
+    // Forward the call to the overwritten receiver method.
+    // -- Arguments [2,N] are left completely untouched since the signature is otherwise identical.
+    // Do not use 'call' here since the stack would be misaligned (8b instead of 16b).
+    // Also the stack visitors wouldn't know how to visit this frame if we used a call.
+    jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)   // tailcall (new method, new receiver, old args...)
+#endif
+
+END_FUNCTION art_quick_lambda_proxy_invoke_handler
+
     /*
      * Called to resolve an imt conflict.
      * rax is a hidden argument that holds the target method's dex method index.
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 4166e22d..ab42d0e 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -255,7 +255,7 @@
 
 inline const char* ArtField::GetName() SHARED_REQUIRES(Locks::mutator_lock_) {
   uint32_t field_index = GetDexFieldIndex();
-  if (UNLIKELY(GetDeclaringClass()->IsProxyClass())) {
+  if (UNLIKELY(GetDeclaringClass()->IsAnyProxyClass())) {
     DCHECK(IsStatic());
     DCHECK_LT(field_index, 2U);
     return field_index == 0 ? "interfaces" : "throws";
@@ -266,7 +266,7 @@
 
 inline const char* ArtField::GetTypeDescriptor() SHARED_REQUIRES(Locks::mutator_lock_) {
   uint32_t field_index = GetDexFieldIndex();
-  if (UNLIKELY(GetDeclaringClass()->IsProxyClass())) {
+  if (UNLIKELY(GetDeclaringClass()->IsAnyProxyClass())) {
     DCHECK(IsStatic());
     DCHECK_LT(field_index, 2U);
     // 0 == Class[] interfaces; 1 == Class[][] throws;
@@ -290,8 +290,8 @@
 inline mirror::Class* ArtField::GetType() {
   const uint32_t field_index = GetDexFieldIndex();
   auto* declaring_class = GetDeclaringClass();
-  if (UNLIKELY(declaring_class->IsProxyClass())) {
-    return ProxyFindSystemClass(GetTypeDescriptor());
+  if (UNLIKELY(declaring_class->IsAnyProxyClass())) {
+    return AnyProxyFindSystemClass(GetTypeDescriptor());
   }
   auto* dex_cache = declaring_class->GetDexCache();
   const DexFile* const dex_file = dex_cache->GetDexFile();
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index 3737e0d..3ac563a 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -69,8 +69,8 @@
   return nullptr;
 }
 
-mirror::Class* ArtField::ProxyFindSystemClass(const char* descriptor) {
-  DCHECK(GetDeclaringClass()->IsProxyClass());
+mirror::Class* ArtField::AnyProxyFindSystemClass(const char* descriptor) {
+  DCHECK(GetDeclaringClass()->IsAnyProxyClass());
   return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor);
 }
 
diff --git a/runtime/art_field.h b/runtime/art_field.h
index a943a34..4ebe6fb 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -191,7 +191,9 @@
   }
 
  private:
-  mirror::Class* ProxyFindSystemClass(const char* descriptor)
+  mirror::Class* AnyProxyFindSystemClass(const char* descriptor)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  mirror::Class* LambdaProxyFindSystemClass(const char* descriptor)
       SHARED_REQUIRES(Locks::mutator_lock_);
   mirror::Class* ResolveGetType(uint32_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_);
   mirror::String* ResolveGetStringName(Thread* self, const DexFile& dex_file, uint32_t string_idx,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index cf548ad..b6e811f 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -292,7 +292,7 @@
 }
 
 inline const char* ArtMethod::GetShorty(uint32_t* out_length) {
-  DCHECK(!IsProxyMethod());
+  DCHECK(!IsProxyMethod() || IsLambdaProxyMethod());  // OK: lambda proxies use parent dex cache.
   const DexFile* dex_file = GetDexFile();
   return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length);
 }
@@ -354,10 +354,31 @@
 }
 
 inline const DexFile::TypeList* ArtMethod::GetParameterTypeList() {
-  DCHECK(!IsProxyMethod());
+  // XX: Do proxy methods have a dex file?  not sure.
   const DexFile* dex_file = GetDexFile();
-  const DexFile::ProtoId& proto = dex_file->GetMethodPrototype(
-      dex_file->GetMethodId(GetDexMethodIndex()));
+  const DexFile::MethodId* method_id = nullptr;
+
+  if (kIsDebugBuild) {
+    if (UNLIKELY(IsProxyMethod())) {
+      // Proxy method case.
+      CHECK(IsLambdaProxyMethod()) << "Cannot GetParameterTypeList for java.lang.reflect.Proxy";
+
+      //
+      // We do not have a method ID, so look up one of the supers we overrode,
+      // it will have the same exact parameter type list as we do.
+
+      // Lambda proxy classes have the dex cache from their single interface parent.
+      // Proxy classes have multiple interface parents, so they use the root dexcache instead.
+      //
+      // For lambda proxy classes only, get the type list data from the parent.
+      // (code happens to look the same as the usual non-proxy path).
+    }
+  }
+
+  method_id = &dex_file->GetMethodId(GetDexMethodIndex());
+  DCHECK(method_id != nullptr);
+
+  const DexFile::ProtoId& proto = dex_file->GetMethodPrototype(*method_id);
   return dex_file->GetProtoParameters(proto);
 }
 
@@ -397,12 +418,20 @@
 }
 
 inline mirror::DexCache* ArtMethod::GetDexCache() {
-  DCHECK(!IsProxyMethod());
+  DCHECK(!IsProxyMethod() || IsLambdaProxyMethod());  // OK: lambda proxies use parent dex cache.
   return GetDeclaringClass()->GetDexCache();
 }
 
 inline bool ArtMethod::IsProxyMethod() {
-  return GetDeclaringClass()->IsProxyClass();
+  return GetDeclaringClass()->IsAnyProxyClass();
+}
+
+inline bool ArtMethod::IsReflectProxyMethod() {
+  return GetDeclaringClass()->IsReflectProxyClass();
+}
+
+inline bool ArtMethod::IsLambdaProxyMethod() {
+  return GetDeclaringClass()->IsLambdaProxyClass();
 }
 
 inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(size_t pointer_size) {
@@ -448,9 +477,9 @@
 void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
   ArtMethod* interface_method = nullptr;
   mirror::Class* klass = declaring_class_.Read();
-  if (UNLIKELY(klass != nullptr && klass->IsProxyClass())) {
+  if (UNLIKELY(klass != nullptr && klass->IsAnyProxyClass())) {
     // For normal methods, dex cache shortcuts will be visited through the declaring class.
-    // However, for proxies we need to keep the interface method alive, so we visit its roots.
+    // However, for any proxies we need to keep the interface method alive, so we visit its roots.
     interface_method = mirror::DexCache::GetElementPtrSize(
         GetDexCacheResolvedMethods(pointer_size),
         GetDexMethodIndex(),
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 5a2d6c3..98f5aee 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -171,8 +171,16 @@
     return (GetAccessFlags() & kAccSynthetic) != 0;
   }
 
+  // Does this method live on a declaring class that is itself any proxy class?
+  // -- Returns true for both java.lang.reflect.Proxy and java.lang.LambdaProxy subclasses.
   bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Does this method live in a java.lang.reflect.Proxy subclass?
+  bool IsReflectProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Does this method live in a java.lang.LambdaProxy subclass?
+  bool IsLambdaProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+
   bool IsPreverified() {
     return (GetAccessFlags() & kAccPreverified) != 0;
   }
@@ -274,7 +282,15 @@
                                             uint32_t name_and_signature_idx)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty)
+  // Invoke this method, passing all the virtual registers in args.
+  // -- args_size must be the size in bytes (not size in words)!
+  // -- shorty must be the method shorty (i.e. it includes the return type).
+  // The result is set when the method finishes execution successfully.
+  void Invoke(Thread* self,
+              uint32_t* args,
+              uint32_t args_size,  // NOTE: size in bytes
+              /*out*/JValue* result,
+              const char* shorty)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   const void* GetEntryPointFromQuickCompiledCode() {
@@ -428,6 +444,9 @@
 
   mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Returns the current method ('this') if this is a regular, non-proxy method.
+  // Otherwise, when this class is a proxy (IsProxyMethod), look-up the original interface's
+  // method (that the proxy is "overriding") and return that.
   ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy(size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index b548dfb..785a9be 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -19,9 +19,12 @@
 
 #if defined(__cplusplus)
 #include "art_method.h"
+#include "lambda/art_lambda_method.h"
+#include "lambda/closure.h"
 #include "gc/allocator/rosalloc.h"
 #include "lock_word.h"
 #include "mirror/class.h"
+#include "mirror/lambda_proxy.h"
 #include "mirror/string.h"
 #include "runtime.h"
 #include "thread.h"
@@ -49,6 +52,8 @@
 #define ADD_TEST_EQ(x, y) CHECK_EQ(x, y);
 #endif
 
+namespace art {
+
 static inline void CheckAsmSupportOffsetsAndSizes() {
 #else
 #define ADD_TEST_EQ(x, y)
@@ -298,9 +303,80 @@
             static_cast<int32_t>(art::gc::allocator::RosAlloc::RunSlotNextOffset()))
 // Assert this so that we can avoid zeroing the next field by installing the class pointer.
 ADD_TEST_EQ(ROSALLOC_SLOT_NEXT_OFFSET, MIRROR_OBJECT_CLASS_OFFSET)
+// Working with raw lambdas (lambda::Closure) in raw memory:
+//
+//     |---------------------|
+//     | ArtLambdaMethod*    |  <-- pointer to lambda art method, has the info like the size.
+//     |---------------------|  <-- 'data offset'
+//     | [ Dynamic Size ]    |  <-- OPTIONAL: only if the ArtLambdaMethod::dynamic_size_ is true.
+//     |---------------------|
+//     | Captured Variables  |
+//     |        ...          |
+//     |---------------------|  <-- total length determined by "dynamic size" if it is present,
+//                                  otherwise by the ArtLambdaMethod::static_size_
+
+// Offset from start of lambda::Closure to the ArtLambdaMethod*.
+#define LAMBDA_CLOSURE_METHOD_OFFSET 0
+ADD_TEST_EQ(static_cast<size_t>(LAMBDA_CLOSURE_METHOD_OFFSET),
+            offsetof(art::lambda::ClosureStorage, lambda_info_))
+// Offset from the start of lambda::Closure to the data (captured vars or dynamic size).
+#define LAMBDA_CLOSURE_DATA_OFFSET __SIZEOF_POINTER__
+ADD_TEST_EQ(static_cast<size_t>(LAMBDA_CLOSURE_DATA_OFFSET),
+            offsetof(art::lambda::ClosureStorage, captured_))
+// Offsets to captured variables intentionally omitted as it needs a runtime branch.
+
+// The size of a lambda closure after it's been compressed down for storage.
+// -- Although a lambda closure is a virtual register pair (64-bit), we only need 32-bit
+//    to track the pointer when we are on 32-bit architectures.
+//    Both the compiler and the runtime therefore compress the closure down for 32-bit archs.
+#define LAMBDA_CLOSURE_COMPRESSED_POINTER_SIZE __SIZEOF_POINTER__
+ADD_TEST_EQ(static_cast<size_t>(LAMBDA_CLOSURE_COMPRESSED_POINTER_SIZE),
+            sizeof(art::lambda::Closure*))
+
+// Working with boxed innate lambdas (as a mirror::Object) in raw memory:
+// --- Note that this layout only applies to lambdas originally made with create-lambda.
+// --- Boxing a lambda created from a new-instance instruction is simply the original object.
+//
+//     |---------------------|
+//     |   object header     |
+//     |---------------------|
+//     | lambda::Closure*    | <-- long on 64-bit, int on 32-bit
+//     |---------------------|
+#define MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET (MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(static_cast<size_t>(MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET),
+            art::mirror::LambdaProxy::GetInstanceFieldOffsetClosure().SizeValue())
+            // Equivalent to (private) offsetof(art::mirror::LambdaProxy, closure_))
+
+// Working with boxed innate lambdas (as a mirror::Object) in raw memory:
+// --- Note that this layout only applies to lambdas originally made with create-lambda.
+// --- Boxing a lambda created from a new-instance instruction is simply the original object.
+//
+//     |---------------------|
+//     |   object header     |
+//     |---------------------|
+//     | lambda::Closure*    | <-- long on 64-bit, int on 32-bit
+//     |---------------------|
+#define ART_LAMBDA_METHOD_ART_METHOD_OFFSET (0)
+ADD_TEST_EQ(static_cast<size_t>(ART_LAMBDA_METHOD_ART_METHOD_OFFSET),
+            art::lambda::ArtLambdaMethod::GetArtMethodOffset())
+
+#if defined(NDEBUG)
+// Release should be faaast. So just jump directly to the lambda method.
+#define LAMBDA_PROXY_SETUP_FRAME 0
+#else
+// Debug can be slower, and we want to get better stack traces. Set up a frame.
+#define LAMBDA_PROXY_SETUP_FRAME 1
+#endif
+
+// For WIP implementation, lambda types are all "longs"
+// which means on a 32-bit implementation we need to fill the argument with 32-bit 0s
+// whenever we invoke a method with a lambda in it.
+// TODO: remove all usages of this once we go to a proper \LambdaType; system.
+#define LAMBDA_INVOKE_USES_LONG 1
 
 #if defined(__cplusplus)
 }  // End of CheckAsmSupportOffsets.
+}  // namespace art
 #endif
 
 #endif  // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index 969f5b9..e2ade07 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -53,6 +53,7 @@
   kAllocatorTagClassTable,
   kAllocatorTagInternTable,
   kAllocatorTagLambdaBoxTable,
+  kAllocatorTagLambdaProxyClassBoxTable,
   kAllocatorTagMaps,
   kAllocatorTagLOS,
   kAllocatorTagSafeMap,
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 70bd398..6ca56f5 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -65,6 +65,7 @@
 Mutex* Locks::trace_lock_ = nullptr;
 Mutex* Locks::unexpected_signal_lock_ = nullptr;
 Mutex* Locks::lambda_table_lock_ = nullptr;
+Mutex* Locks::lambda_class_table_lock_ = nullptr;
 Uninterruptible Roles::uninterruptible_;
 
 struct AllMutexData {
@@ -954,6 +955,7 @@
     DCHECK(trace_lock_ != nullptr);
     DCHECK(unexpected_signal_lock_ != nullptr);
     DCHECK(lambda_table_lock_ != nullptr);
+    DCHECK(lambda_class_table_lock_ != nullptr);
   } else {
     // Create global locks in level order from highest lock level to lowest.
     LockLevel current_lock_level = kInstrumentEntrypointsLock;
@@ -1072,6 +1074,10 @@
     DCHECK(lambda_table_lock_ == nullptr);
     lambda_table_lock_ = new Mutex("lambda table lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kLambdaClassTableLock);
+    DCHECK(lambda_class_table_lock_ == nullptr);
+    lambda_class_table_lock_ = new Mutex("lambda class table lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
     DCHECK(abort_lock_ == nullptr);
     abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index d4c9057..e2d7062 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -60,6 +60,7 @@
   kUnexpectedSignalLock,
   kThreadSuspendCountLock,
   kAbortLock,
+  kLambdaClassTableLock,
   kLambdaTableLock,
   kJdwpSocketLock,
   kRegionSpaceRegionLock,
@@ -692,6 +693,10 @@
   // Allow reader-writer mutual exclusion on the boxed table of lambda objects.
   // TODO: this should be a RW mutex lock, except that ConditionVariables don't work with it.
   static Mutex* lambda_table_lock_ ACQUIRED_AFTER(mutator_lock_);
+
+  // Allow reader-writer mutual exclusion on the boxed table of lambda proxy classes.
+  // TODO: this should be a RW mutex lock, except that ConditionVariables don't work with it.
+  static Mutex* lambda_class_table_lock_ ACQUIRED_AFTER(lambda_table_lock_);
 };
 
 class Roles {
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index 0949619..324ab21 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -149,6 +149,13 @@
   return it != container.end();
 }
 
+// const char* compare function suitable for std::map or std::set.
+struct CStringLess {
+  bool operator()(const char* lhs, const char* rhs) const {
+    return strcmp(lhs, rhs) < 0;
+  }
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_STL_UTIL_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2dd2a83..8a0d8d4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -56,6 +56,7 @@
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
+#include "lambda/box_class_table.h"
 #include "leb128.h"
 #include "linear_alloc.h"
 #include "mirror/class.h"
@@ -64,6 +65,7 @@
 #include "mirror/dex_cache-inl.h"
 #include "mirror/field.h"
 #include "mirror/iftable-inl.h"
+#include "mirror/lambda_proxy.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
@@ -581,6 +583,9 @@
   // Create java.lang.reflect.Proxy root.
   SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
 
+  // Create java.lang.LambdaProxy root.
+  SetClassRoot(kJavaLangLambdaProxy, FindSystemClass(self, "Ljava/lang/LambdaProxy;"));
+
   // Create java.lang.reflect.Field.class root.
   auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
   CHECK(class_root != nullptr);
@@ -1257,6 +1262,7 @@
   }
   delete data.allocator;
   delete data.class_table;
+  delete data.lambda_box_class_table;
 }
 
 mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
@@ -1898,8 +1904,10 @@
 // Special case to get oat code without overwriting a trampoline.
 const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
   CHECK(method->IsInvokable()) << PrettyMethod(method);
-  if (method->IsProxyMethod()) {
+  if (method->IsReflectProxyMethod()) {
     return GetQuickProxyInvokeHandler();
+  } else if (method->IsLambdaProxyMethod()) {
+    return GetQuickLambdaProxyInvokeHandler();
   }
   bool found;
   OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
@@ -3257,7 +3265,7 @@
   klass->SetName(soa.Decode<mirror::String*>(name));
   klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
   mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
-  std::string descriptor(GetDescriptorForProxy(klass.Get()));
+  std::string descriptor(GetDescriptorForAnyProxy(klass.Get()));
   const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
 
   // Needs to be before we insert the class so that the allocator field is set.
@@ -3377,23 +3385,228 @@
                                                decoded_name->ToModifiedUtf8().c_str()));
     CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
 
-    CHECK_EQ(klass.Get()->GetInterfaces(),
+    CHECK_EQ(klass.Get()->GetInterfacesForAnyProxy(),
              soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
-    CHECK_EQ(klass.Get()->GetThrows(),
+    CHECK_EQ(klass.Get()->GetThrowsForAnyProxy(),
              soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
   }
   return klass.Get();
 }
 
-std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) {
-  DCHECK(proxy_class->IsProxyClass());
+mirror::Class* ClassLinker::CreateLambdaProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+                                                   jstring name,
+                                                   jobjectArray interfaces,
+                                                   jobject loader,
+                                                   jobjectArray methods,
+                                                   jobjectArray throws,
+                                                   bool* already_exists) {
+  DCHECK(already_exists != nullptr);
+  *already_exists = false;
+
+  Thread* self = soa.Self();
+  StackHandleScope<10> hs(self);
+
+  // Allocate a new java.lang.Class object for a mirror::Proxy.
+  MutableHandle<mirror::Class> klass =
+      hs.NewHandle(AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)));
+  if (klass.Get() == nullptr) {
+    CHECK(self->IsExceptionPending());  // OOME.
+    return nullptr;
+  }
+  DCHECK(klass->GetClass() != nullptr);
+  klass->SetObjectSize(sizeof(mirror::LambdaProxy));
+
+  // Set the class access flags incl. preverified, so we do not try to set the flag on the methods.
+  klass->SetAccessFlags(kAccClassIsLambdaProxy | kAccPublic | kAccFinal | kAccPreverified);
+  klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader));
+  DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
+  klass->SetName(soa.Decode<mirror::String*>(name));
+  klass->SetDexCache(GetClassRoot(kJavaLangLambdaProxy)->GetDexCache());
+  // Set the status to be just before after loading it, but before anything is resolved.
+  mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
+  // Convert "foo.bar.baz" string to "Lfoo/bar/baz;"
+  std::string type_descriptor(GetDescriptorForAnyProxy(klass.Get()));
+
+  mirror::Class* existing;
+  {
+    const size_t hash = ComputeModifiedUtf8Hash(type_descriptor.c_str());
+
+    // Insert the class before loading the fields as the field roots
+    // (ArtField::declaring_class_) are only visited from the class
+    // table. There can't be any suspend points between inserting the
+    // class and setting the field arrays below.
+    existing = InsertClass(type_descriptor.c_str(), klass.Get(), hash);
+  }
+  if (UNLIKELY(existing != nullptr)) {
+    // We had already made the lambda proxy previously. Return it.
+
+    *already_exists = true;
+    return existing;
+    // Let the GC clean up the class we had already allocated but isn't being used.
+  }
+
+  // Needs to be after we insert the class so that the allocator field is set.
+  LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(klass->GetClassLoader());
+
+  // Instance fields are inherited, but we add a couple of static fields...
+  LengthPrefixedArray<ArtField>* sfields =
+      AllocArtFieldArray(self, allocator, mirror::LambdaProxy::kStaticFieldCount);
+  klass->SetSFieldsPtr(sfields);
+
+  // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by
+  // our proxy, so Class.getInterfaces doesn't return the flattened set.
+  // -- private static java.lang.Class[] interfaces;  // list of declared interfaces
+  ArtField& interfaces_sfield = sfields->At(mirror::LambdaProxy::kStaticFieldIndexInterfaces);
+  interfaces_sfield.SetDexFieldIndex(mirror::LambdaProxy::kStaticFieldIndexInterfaces);
+  interfaces_sfield.SetDeclaringClass(klass.Get());
+  interfaces_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+
+  // 2. Create a static field 'throws' that holds the classes of exceptions thrown by our methods.
+  // This is returned by java.lang.reflect.Method#getExceptionTypes()
+  // --- private static java.lang.Class[][] throws;  // maps vtable id to list of classes.
+  ArtField& throws_sfield = sfields->At(mirror::LambdaProxy::kStaticFieldIndexThrows);
+  throws_sfield.SetDexFieldIndex(mirror::LambdaProxy::kStaticFieldIndexThrows);
+  throws_sfield.SetDeclaringClass(klass.Get());
+  throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+
+  // Set up the Constructor method.
+  {
+    // Lambda proxies have 1 direct method, the constructor.
+    static constexpr size_t kNumDirectMethods = 1;
+    LengthPrefixedArray<ArtMethod>* directs = AllocArtMethodArray(self,
+                                                                  allocator,
+                                                                  kNumDirectMethods);
+    // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we
+    // want to throw OOM in the future.
+    if (UNLIKELY(directs == nullptr)) {
+      self->AssertPendingOOMException();
+      return nullptr;
+    }
+    klass->SetDirectMethodsPtr(directs);
+    CreateLambdaProxyConstructor(klass, klass->GetDirectMethodUnchecked(0, image_pointer_size_));
+  }
+
+  // Create virtual method using specified prototypes.
+  auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods));
+  DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass())
+      << PrettyClass(h_methods->GetClass());
+  const size_t num_virtual_methods = h_methods->GetLength();
+  auto* virtuals = AllocArtMethodArray(self, allocator, num_virtual_methods);
+  // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we
+  // want to throw OOM in the future.
+  if (UNLIKELY(virtuals == nullptr)) {
+    self->AssertPendingOOMException();
+    return nullptr;
+  }
+  klass->SetVirtualMethodsPtr(virtuals);
+  size_t abstract_methods = 0;
+  for (size_t i = 0; i < num_virtual_methods; ++i) {
+    ArtMethod* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
+    ArtMethod* prototype = h_methods->Get(i)->GetArtMethod();
+    if (UNLIKELY((prototype->GetAccessFlags() & kAccDefault) != 0)) {
+      UNIMPLEMENTED(FATAL) << "Lambda proxies don't support default methods yet";
+    }
+    if (prototype->IsAbstract()) {
+      abstract_methods++;
+    }
+    VLOG(class_linker) << "Creating lambda proxy method for " << PrettyMethod(prototype);
+
+    CreateLambdaProxyMethod(klass, prototype, virtual_method);
+    DCHECK(virtual_method->GetDeclaringClass() != nullptr);
+    DCHECK(prototype->GetDeclaringClass() != nullptr);
+  }
+  // Ignore any methods from Object and default methods, it doesn't matter.
+  // Sanity check that the prototype interface is indeed compatible with lambdas.
+  DCHECK_EQ(abstract_methods, 1u)
+      << "Interface must be a single-abstract-method type" << PrettyClass(klass.Get());
+
+  // The super class is java.lang.LambdaProxy
+  klass->SetSuperClass(GetClassRoot(kJavaLangLambdaProxy));
+  // Now effectively in the loaded state.
+  mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, self);
+  self->AssertNoPendingException();
+
+  MutableHandle<mirror::Class> new_class = hs.NewHandle<mirror::Class>(nullptr);
+  {
+    // Must hold lock on object when resolved.
+    ObjectLock<mirror::Class> resolution_lock(self, klass);
+    // Link the fields and virtual methods, creating vtable and iftables.
+    // The new class will replace the old one in the class table.
+    Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
+        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)));
+
+    {
+      DCHECK_EQ(1, h_interfaces->GetLength()) << "Lambda proxies must implement 1 interface only";
+      mirror::Class* single_abstract_interface = h_interfaces->Get(0);
+      DCHECK(single_abstract_interface != nullptr);
+
+      // Use the dex cache from the interface, which will enable most of the
+      // dex-using mechanisms on the class and its methods will work.
+      klass->SetDexCache(single_abstract_interface->GetDexCache());
+    }
+
+    if (!LinkClass(self, type_descriptor.c_str(), klass, h_interfaces, &new_class)) {
+      mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+      return nullptr;
+    }
+  }
+  CHECK(klass->IsRetired());
+  CHECK_NE(klass.Get(), new_class.Get());
+  klass.Assign(new_class.Get());
+
+  CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get());
+  interfaces_sfield.SetObject<false>(klass.Get(),
+                                     soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+
+  CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get());
+  throws_sfield.SetObject<false>(
+      klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+
+  {
+    // Lock on klass is released. Lock new class object.
+    ObjectLock<mirror::Class> initialization_lock(self, klass);
+    mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self);
+  }
+
+  // Sanity checks
+  if (kIsDebugBuild) {
+    CHECK(klass->GetIFieldsPtr() == nullptr);
+    CheckLambdaProxyConstructor(klass->GetDirectMethod(0, image_pointer_size_));
+
+    for (size_t i = 0; i < num_virtual_methods; ++i) {
+      ArtMethod* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
+      ArtMethod* prototype = h_methods->Get(i++)->GetArtMethod();
+      CheckLambdaProxyMethod(virtual_method, prototype);
+    }
+
+    StackHandleScope<1> hs2(self);
+    Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String*>(name));
+    std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces",
+                                                   decoded_name->ToModifiedUtf8().c_str()));
+    CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name);
+
+    std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws",
+                                               decoded_name->ToModifiedUtf8().c_str()));
+    CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
+
+    CHECK_EQ(klass.Get()->GetInterfacesForAnyProxy(),
+             soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+    CHECK_EQ(klass.Get()->GetThrowsForAnyProxy(),
+             soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
+  }
+  return klass.Get();
+}
+
+std::string ClassLinker::GetDescriptorForAnyProxy(mirror::Class* proxy_class) {
+  DCHECK(proxy_class != nullptr);
+  DCHECK(proxy_class->IsAnyProxyClass());
   mirror::String* name = proxy_class->GetName();
   DCHECK(name != nullptr);
   return DotToDescriptor(name->ToModifiedUtf8().c_str());
 }
 
 ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) {
-  DCHECK(proxy_class->IsProxyClass());
+  DCHECK(proxy_class->IsAnyProxyClass());
   DCHECK(proxy_method->IsProxyMethod());
   {
     Thread* const self = Thread::Current();
@@ -3421,7 +3634,7 @@
 
 void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
   // Create constructor for Proxy that must initialize the method.
-  CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 16u);
+  CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
   ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
       2, image_pointer_size_);
   // Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
@@ -3437,6 +3650,38 @@
   out->SetDeclaringClass(klass.Get());
 }
 
+void ClassLinker::CreateLambdaProxyConstructor(Handle<mirror::Class> klass,
+                                               /*out*/ArtMethod* method_constructor) {
+  DCHECK(klass.Get() != nullptr);
+  DCHECK(method_constructor != nullptr);
+
+  // Create constructor for Proxy that must initialize the method.
+  // Lambda proxy superclass only has 1 direct method, the constructor (<init>()V)
+  CHECK_EQ(GetClassRoot(kJavaLangLambdaProxy)->NumDirectMethods(),
+           mirror::LambdaProxy::kDirectMethodCount);
+  // Get the constructor method.
+  ArtMethod* proxy_constructor = GetClassRoot(kJavaLangLambdaProxy)->GetDirectMethodUnchecked(
+      mirror::LambdaProxy::kDirectMethodIndexConstructor,
+      image_pointer_size_);
+
+  // Verify constructor method is indeed a constructor.
+  CHECK(proxy_constructor != nullptr);
+
+  // Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
+  // constructor method.
+  GetClassRoot(kJavaLangLambdaProxy)->GetDexCache()->SetResolvedMethod(
+      proxy_constructor->GetDexMethodIndex(),
+      proxy_constructor,
+      image_pointer_size_);
+
+  // Clone the existing constructor of LambdaProxy
+  // (our constructor would just invoke it so steal its code_ too).
+  method_constructor->CopyFrom(proxy_constructor, image_pointer_size_);
+  // Make this constructor public and fix the class to be our LambdaProxy version
+  method_constructor->SetAccessFlags((method_constructor->GetAccessFlags() & ~kAccProtected) | kAccPublic);
+  method_constructor->SetDeclaringClass(klass.Get());
+}
+
 void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const {
   CHECK(constructor->IsConstructor());
   auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_);
@@ -3445,6 +3690,14 @@
   DCHECK(constructor->IsPublic());
 }
 
+void ClassLinker::CheckLambdaProxyConstructor(ArtMethod* constructor) const {
+  CHECK(constructor->IsConstructor());
+  auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_);
+  CHECK_STREQ(np->GetName(), "<init>");
+  CHECK_STREQ(np->GetSignature().ToString().c_str(), "()V");
+  DCHECK(constructor->IsPublic());
+}
+
 void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype,
                                     ArtMethod* out) {
   // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
@@ -3456,6 +3709,7 @@
     dex_cache->SetResolvedMethod(
         prototype->GetDexMethodIndex(), prototype, image_pointer_size_);
   }
+
   // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize
   // as necessary
   DCHECK(out != nullptr);
@@ -3471,6 +3725,42 @@
   out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
 }
 
+void ClassLinker::CreateLambdaProxyMethod(Handle<mirror::Class> klass,
+                                          ArtMethod* prototype,
+                                          ArtMethod* out) {
+  DCHECK(prototype != nullptr);
+  DCHECK(out != nullptr);
+
+  // DO NOT go through the proxy invoke handler for the default methods. They have no idea
+  // how to handle the raw closure, so they must get the regular object when invoked.
+  CHECK_EQ(prototype->GetAccessFlags() & kAccDefault, 0u) << "Default methods must not be proxied";
+
+  // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
+  // prototype method
+  auto* dex_cache = prototype->GetDeclaringClass()->GetDexCache();
+  // Avoid dirtying the dex cache unless we need to.
+  if (dex_cache->GetResolvedMethod(prototype->GetDexMethodIndex(), image_pointer_size_) !=
+      prototype) {
+    dex_cache->SetResolvedMethod(
+        prototype->GetDexMethodIndex(), prototype, image_pointer_size_);
+  }
+  // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize
+  // as necessary
+  out->CopyFrom(prototype, image_pointer_size_);
+
+  // Set class to be the concrete proxy class and clear the abstract flag, modify exceptions to
+  // the intersection of throw exceptions as defined in Proxy
+  out->SetDeclaringClass(klass.Get());
+  out->SetAccessFlags((out->GetAccessFlags() & ~kAccAbstract) | kAccFinal);
+
+  // Setting the entry point isn't safe for AOT since ASLR loads it anywhere at runtime.
+  CHECK(!Runtime::Current()->IsAotCompiler());
+
+  // At runtime the method looks like a reference and argument saving method, clone the code
+  // related parameters from this method.
+  out->SetEntryPointFromQuickCompiledCode(GetQuickLambdaProxyInvokeHandler());
+}
+
 void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
   // Basic sanity
   CHECK(!prototype->IsFinal());
@@ -3492,6 +3782,11 @@
            prototype->GetReturnType(true /* resolve */, image_pointer_size_));
 }
 
+void ClassLinker::CheckLambdaProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
+  // same as above.
+  return CheckProxyMethod(method, prototype);
+}
+
 bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
                                        bool can_init_parents) {
   if (can_init_statics && can_init_parents) {
@@ -4123,7 +4418,9 @@
     class_loader->SetClassTable(data.class_table);
     // Should have been set when we registered the dex file.
     data.allocator = class_loader->GetAllocator();
-    CHECK(data.allocator != nullptr);
+    CHECK(class_loader->GetLambdaProxyCache() == nullptr);
+    data.lambda_box_class_table = new lambda::BoxClassTable();
+    class_loader->SetLambdaProxyCache(data.lambda_box_class_table);
     class_loaders_.push_back(data);
   }
   return class_table;
@@ -6566,6 +6863,7 @@
     "Ljava/lang/reflect/Field;",
     "Ljava/lang/reflect/Method;",
     "Ljava/lang/reflect/Proxy;",
+    "Ljava/lang/LambdaProxy;",
     "[Ljava/lang/String;",
     "[Ljava/lang/reflect/Constructor;",
     "[Ljava/lang/reflect/Field;",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 29aac31..f073cd8 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -40,6 +40,11 @@
   class ImageSpace;
 }  // namespace space
 }  // namespace gc
+
+namespace lambda {
+  class BoxClassTable;
+}  // namespace lambda
+
 namespace mirror {
   class ClassLoader;
   class DexCache;
@@ -82,6 +87,7 @@
     kJavaLangReflectField,
     kJavaLangReflectMethod,
     kJavaLangReflectProxy,
+    kJavaLangLambdaProxy,
     kJavaLangStringArrayClass,
     kJavaLangReflectConstructorArrayClass,
     kJavaLangReflectFieldArrayClass,
@@ -424,12 +430,46 @@
                                   jobjectArray methods,
                                   jobjectArray throws)
       SHARED_REQUIRES(Locks::mutator_lock_);
-  std::string GetDescriptorForProxy(mirror::Class* proxy_class)
+
+  // Get the long type descriptor, e.g. "LProxyName$1234;" for the requested proxy class.
+  static std::string GetDescriptorForAnyProxy(mirror::Class* proxy_class)
       SHARED_REQUIRES(Locks::mutator_lock_);
   ArtMethod* FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method)
       REQUIRES(!dex_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Create a lambda proxy class.
+  // -- Nominally used when boxing an innate lambda, since that has no corresponding class.
+  //
+  // * name must be a fully-qualified class name (and dotted), e.g. "java.lang.Runnable"
+  // * interfaces is an array of java.lang.Class for interfaces that will be the supertype
+  //   (note that there must be exactly 1 element here for a lambda interface since lambda
+  //   types can only target 1 interface).
+  // * loader must be a java.lang.ClassLoader where the proxy class will be created
+  // * methods must be an array of java.lang.reflect.Method that consists of the
+  //   deduplicated methods from all of the interfaces specified.
+  // * throws must be an array of java.lang.Class[] where each index corresponds to that of
+  //   methods, and it signifies the "throws" keyword of each method
+  //   (this is not directly used by the runtime itself, but it is available via reflection).
+  //
+  // Returns a non-null pointer to a class upon success, otherwise null and throws an exception.
+  //
+  // If the class was already created previously (with the same name but potentially different
+  // parameters), already_exists is set to true; otherwise already_exists is set to false.
+  // The already_exists value is undefined when an exception was thrown.
+  //
+  // Sidenote: interfaces is an array to simplify the libcore code which creates a Java
+  // array in an attempt to reduce code duplication.
+  // TODO: this should probably also take the target single-abstract-method as well.
+  mirror::Class* CreateLambdaProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+                                        jstring name,
+                                        jobjectArray interfaces,
+                                        jobject loader,
+                                        jobjectArray methods,
+                                        jobjectArray throws,
+                                        /*out*/bool* already_exists)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // Get the oat code for a method when its class isn't yet initialized
   const void* GetQuickOatCodeFor(ArtMethod* method)
       SHARED_REQUIRES(Locks::mutator_lock_);
@@ -573,6 +613,7 @@
     jweak weak_root;  // Weak root to enable class unloading.
     ClassTable* class_table;
     LinearAlloc* allocator;
+    lambda::BoxClassTable* lambda_box_class_table;
   };
 
   // Ensures that the supertype of 'klass' ('supertype') is verified. Returns false and throws
@@ -907,8 +948,12 @@
 
   void CheckProxyConstructor(ArtMethod* constructor) const
       SHARED_REQUIRES(Locks::mutator_lock_);
+  void CheckLambdaProxyConstructor(ArtMethod* constructor) const
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const
       SHARED_REQUIRES(Locks::mutator_lock_);
+  void CheckLambdaProxyMethod(ArtMethod* method, ArtMethod* prototype) const
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // For use by ImageWriter to find DexCaches for its roots
   ReaderWriterMutex* DexLock()
@@ -926,9 +971,19 @@
 
   void CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out)
       SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Copy the constructor from java.lang.LambdaProxy into the 'klass'.
+  // The copy is written into 'method_constructor'.
+  void CreateLambdaProxyConstructor(Handle<mirror::Class> klass,
+                                    /*out*/ArtMethod* method_constructor)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   void CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  void CreateLambdaProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   // Ensures that methods have the kAccPreverified bit set. We use the kAccPreverfied bit on the
   // class access flags to determine whether this has been done before.
   void EnsurePreverifiedMethods(Handle<mirror::Class> c)
@@ -940,7 +995,10 @@
   // Returns null if not found.
   ClassTable* ClassTableForClassLoader(mirror::ClassLoader* class_loader)
       SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_);
-  // Insert a new class table if not found.
+
+  // Insert a new class table if not found. Uses bootclasspath if class_loader is null.
+  // Returns either the existing table, or the new one if there wasn't one previously
+  // (the return value is always non-null).
   ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader)
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 2c086c5..4a9db1d 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -31,6 +31,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/field.h"
+#include "mirror/lambda_proxy.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -552,6 +553,7 @@
   ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") {
     addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, allocator_), "allocator");
     addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, class_table_), "classTable");
+    addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, lambda_proxy_cache_), "lambdaProxyCache");
     addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, packages_), "packages");
     addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, parent_), "parent");
     addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, proxyCache_), "proxyCache");
@@ -564,6 +566,13 @@
   };
 };
 
+struct LambdaProxyOffsets : public CheckOffsets<mirror::LambdaProxy> {
+  LambdaProxyOffsets() : CheckOffsets<mirror::LambdaProxy>(false, "Ljava/lang/LambdaProxy;") {
+    addOffset(OFFSETOF_MEMBER(mirror::LambdaProxy, closure_), "closure");
+  };
+};
+
+
 struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> {
   DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") {
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
@@ -639,6 +648,7 @@
   EXPECT_TRUE(StackTraceElementOffsets().Check());
   EXPECT_TRUE(ClassLoaderOffsets().Check());
   EXPECT_TRUE(ProxyOffsets().Check());
+  EXPECT_TRUE(LambdaProxyOffsets().Check());
   EXPECT_TRUE(DexCacheOffsets().Check());
   EXPECT_TRUE(ReferenceOffsets().Check());
   EXPECT_TRUE(FinalizerReferenceOffsets().Check());
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f705a50..e84313c 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -55,7 +55,6 @@
   // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
   // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
   // everything else. In case you want to see all messages, comment out the line.
-  setenv("ANDROID_LOG_TAGS", "*:e", 1);
 
   art::InitLogging(argv);
   LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 70096f5..4163e2e 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1870,10 +1870,10 @@
         Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
         ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField(
             klass->GetDexFile(), index, dex_cache, class_loader, true);
-        Handle<mirror::Class> field_class(hs.NewHandle(enum_field->GetDeclaringClass()));
         if (enum_field == nullptr) {
           return false;
         } else {
+          Handle<mirror::Class> field_class(hs.NewHandle(enum_field->GetDeclaringClass()));
           Runtime::Current()->GetClassLinker()->EnsureInitialized(self, field_class, true, true);
           element_object = enum_field->GetObject(field_class.Get());
           set_object = true;
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 87e29ae..2a92226 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -313,7 +313,7 @@
           reinterpret_cast<uintptr_t>(virtual_methods)) / method_size;
       CHECK_LT(throws_index, static_cast<int>(num_virtuals));
       mirror::ObjectArray<mirror::Class>* declared_exceptions =
-          proxy_class->GetThrows()->Get(throws_index);
+          proxy_class->GetThrowsForAnyProxy()->Get(throws_index);
       mirror::Class* exception_class = exception->GetClass();
       bool declares_exception = false;
       for (int32_t i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index abf9ac4..8c2dc3e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,9 +23,12 @@
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
+#include "lambda/closure.h"
+#include "lambda/art_lambda_method.h"
 #include "method_reference.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
+#include "mirror/lambda_proxy.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
@@ -294,7 +297,8 @@
   // 1st GPR.
   static mirror::Object* GetProxyThisObject(ArtMethod** sp)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    CHECK((*sp)->IsProxyMethod());
+    // TODO: Lambda proxies only set up a frame when debugging
+    CHECK((*sp)->IsReflectProxyMethod() || ((*sp)->IsLambdaProxyMethod() /*&& kIsDebugBuild*/));
     CHECK_GT(kNumQuickGprArgs, 0u);
     constexpr uint32_t kThisGprIndex = 0u;  // 'this' is in the 1st GPR.
     size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
@@ -834,8 +838,9 @@
 extern "C" uint64_t artQuickProxyInvokeHandler(
     ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp)
     SHARED_REQUIRES(Locks::mutator_lock_) {
-  DCHECK(proxy_method->IsProxyMethod()) << PrettyMethod(proxy_method);
-  DCHECK(receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method);
+  DCHECK(proxy_method->GetDeclaringClass()->IsReflectProxyClass()) << PrettyMethod(proxy_method);
+  DCHECK(proxy_method->IsReflectProxyMethod()) << PrettyMethod(proxy_method);
+  DCHECK(receiver->GetClass()->IsReflectProxyClass()) << PrettyMethod(proxy_method);
   // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
   const char* old_cause =
       self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
@@ -878,6 +883,175 @@
   return result.GetJ();
 }
 
+extern "C" uint64_t artQuickLambdaProxyInvokeHandler(
+    ArtMethod* proxy_method, mirror::LambdaProxy* receiver, Thread* self, ArtMethod** sp)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  using lambda::ShortyFieldType;
+
+  DCHECK(proxy_method->GetDeclaringClass()->IsLambdaProxyClass()) << PrettyMethod(proxy_method);
+  DCHECK(proxy_method->IsLambdaProxyMethod()) << PrettyMethod(proxy_method);
+  DCHECK(receiver->GetClass()->IsLambdaProxyClass()) << PrettyMethod(proxy_method);
+
+  lambda::Closure* lambda_closure = receiver->GetClosure();
+  DCHECK(lambda_closure != nullptr);  // Should've NPEd during the invoke-interface.
+  // Learned lambdas have their own implementation of the SAM, they must not go through here.
+  DCHECK(lambda_closure->GetLambdaInfo()->IsInnateLambda());
+  ArtMethod* target_method = lambda_closure->GetTargetMethod();
+
+  // Lambda targets are always static.
+  // TODO: This should really be a target_method->IsLambda(), once we add the access flag.
+  CHECK(target_method->IsStatic()) << PrettyMethod(proxy_method) << " "
+                                   << PrettyMethod(target_method);
+
+  // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
+  const char* old_cause =
+      self->StartAssertNoThreadSuspension("Adding to IRT/SF lambda proxy object arguments");
+  // Register the top of the managed stack, making stack crawlable.
+  DCHECK_EQ((*sp), proxy_method) << PrettyMethod(proxy_method);
+  self->VerifyStack();
+  // Start new JNI local reference state.
+  JNIEnvExt* env = self->GetJniEnv();
+  ScopedObjectAccessUnchecked soa(env);
+
+  // Placing arguments into args vector and remove the receiver.
+  ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy(sizeof(void*));
+  CHECK(!non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " "
+                                       << PrettyMethod(non_proxy_method);
+  uint32_t shorty_len = 0;
+  const char* shorty = non_proxy_method->GetShorty(/*out*/&shorty_len);
+
+  std::vector<jvalue> args;
+  // Make a quick visitor so we can restore the refs incase they move after a GC.
+  BuildQuickArgumentVisitor local_ref_visitor(sp,
+                                              false /*is_static*/,
+                                              shorty,
+                                              shorty_len,
+                                              &soa,
+                                              /*out*/&args);
+  local_ref_visitor.VisitArguments();
+
+  static_assert(lambda::kClosureIsStoredAsLong,
+                "Need to update this code once closures are no "
+                "longer treated as a 'long' in quick abi");
+
+  // Allocate one vreg more than usual because we need to convert our
+  // receiver Object (1 vreg) into a long (2 vregs).
+  // TODO: Ugly... move to traits instead?
+  const uint32_t first_arg_reg = ShortyFieldType(ShortyFieldType::kLambda).GetVirtualRegisterCount()
+        - ShortyFieldType(ShortyFieldType::kObject).GetVirtualRegisterCount();
+  const uint32_t num_vregs = lambda_closure->GetLambdaInfo()->GetArgumentVRegCount();
+  DCHECK_GE(num_vregs, first_arg_reg);
+  if (kIsDebugBuild) {
+    const char* method_shorty = non_proxy_method->GetShorty();
+    DCHECK_NE(*method_shorty, '\0') << method_shorty;
+    const char* arg_shorty = method_shorty + 1;  // Skip return type.
+
+    // Proxy method should have an object (1 vreg) receiver,
+    // Lambda method should have a lambda (2 vregs) receiver.
+    // -- All other args are the same as before.
+    // -- Make sure vreg count is what we thought it was.
+    uint32_t non_proxy_num_vregs =
+        ShortyFieldType::CountVirtualRegistersRequired(arg_shorty)  // doesn't count receiver
+        + ShortyFieldType(ShortyFieldType::kObject).GetVirtualRegisterCount();  // implicit receiver
+
+    CHECK_EQ(non_proxy_num_vregs + first_arg_reg, num_vregs)
+        << PrettyMethod(non_proxy_method) << " " << PrettyMethod(lambda_closure->GetTargetMethod());
+  }
+
+  ShadowFrameAllocaUniquePtr shadow_frame = CREATE_SHADOW_FRAME(num_vregs,
+                                                                /*link*/nullptr,
+                                                                target_method,
+                                                                /*dex_pc*/0);
+
+  // Copy our proxy method caller's arguments into this ShadowFrame.
+  BuildQuickShadowFrameVisitor local_sf_visitor(sp,
+                                                /*is_static*/false,
+                                                shorty,
+                                                shorty_len,
+                                                shadow_frame.get(),
+                                                first_arg_reg);
+
+  local_sf_visitor.VisitArguments();
+  // Now fix up the arguments, with each ArgK being a vreg:
+
+  // (Before):
+  // Arg0 = proxy receiver (LambdaProxy)
+  // Arg1 = first-user defined argument
+  // Arg2 = second user-defined argument
+  // ....
+  // ArgN = ...
+
+  // (After)
+  // Arg0 = closure (hi)
+  // Arg1 =  closure (lo) = 0x00 on 32-bit
+  // Arg2 = <?> (first user-defined argument)
+  // Arg3 = <?> (first user-defined argument)
+  // ...
+  // argN+1 = ...
+
+  // Transformation diagram:
+  /*
+     Arg0  Arg2  Arg3 ... ArgN
+       |       \     \        \
+       |        \     \        \
+     ClHi  ClLo  Arg2  Arg3 ... ArgN:
+   */
+
+  // 1) memmove vregs 1-N into 2-N+1
+  uint32_t* shadow_frame_vregs = shadow_frame->GetVRegArgs(/*i*/0);
+  if (lambda::kClosureIsStoredAsLong ||
+      sizeof(void*) != sizeof(mirror::CompressedReference<mirror::LambdaProxy>)) {
+    // Suspending here would be very bad since we are doing a raw memmove
+
+    // Move the primitive vregs over.
+    {
+      size_t shadow_frame_vregs_size = num_vregs;
+      memmove(shadow_frame_vregs + first_arg_reg,
+              shadow_frame_vregs,
+              shadow_frame_vregs_size - first_arg_reg);
+    }
+
+    // Move the reference vregs over.
+    if (LIKELY(shadow_frame->HasReferenceArray())) {
+      uint32_t* shadow_frame_references = shadow_frame_vregs + num_vregs;
+      size_t shadow_frame_references_size = num_vregs;
+      memmove(shadow_frame_references + first_arg_reg,
+              shadow_frame_references,
+              shadow_frame_references_size - first_arg_reg);
+    }
+
+    static_assert(lambda::kClosureSupportsReadBarrier == false,
+                  "Using this memmove code with a read barrier GC seems like it could be unsafe.");
+
+    static_assert(sizeof(mirror::CompressedReference<mirror::LambdaProxy>) == sizeof(uint32_t),
+                  "This block of code assumes a compressed reference fits into exactly 1 vreg");
+  }
+  // 2) replace proxy receiver with lambda
+  shadow_frame->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<uintptr_t>(lambda_closure)));
+
+  // OK: After we do the invoke, the target method takes over managing the arguments
+  //     and we won't ever access the shadow frame again (if any references moved).
+  self->EndAssertNoThreadSuspension(old_cause);
+
+  // The shadow frame vreg contents are now 'owned' by the Invoke method, and
+  // will be managed by it during a GC despite being a raw uint32_t array.
+  // We however have no guarantee that it is updated on the way out, so do not read out of the
+  // shadow frame after this call.
+  JValue result;
+  target_method->Invoke(self,
+                        shadow_frame_vregs,
+                        num_vregs * sizeof(uint32_t),
+                        /*out*/&result,
+                        target_method->GetShorty());
+
+  // Restore references on the proxy caller stack frame which might have moved.
+  // -- This is necessary because the QuickFrameInfo is just the generic runtime "RefsAndArgs"
+  //    which means that the regular stack visitor wouldn't know how to GC-move any references
+  //    that we spilled ourselves in the proxy stub.
+  local_ref_visitor.FixupReferences();
+  return result.GetJ();
+}
+
 // Read object references held in arguments from quick frames and place in a JNI local references,
 // so they don't get garbage collected.
 class RememberForGcArgumentVisitor FINAL : public QuickArgumentVisitor {
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index 2842c5a..1ef7585 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -17,6 +17,10 @@
 #ifndef ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
 #define ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
 
+// Define entry points to assembly routines.
+// All extern "C" functions here are defined in a corresponding assembly-only file.
+// The exact file paths are runtime/arch/$ISA/quick_entrypoints_$ISA.s
+
 namespace art {
 
 #ifndef BUILDING_LIBART
@@ -52,6 +56,13 @@
   return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
 }
 
+// Return the address of quick stub code for handling transitions into the lambda proxy
+// invoke handler.
+extern "C" void art_quick_lambda_proxy_invoke_handler();
+static inline const void* GetQuickLambdaProxyInvokeHandler() {
+  return reinterpret_cast<const void*>(art_quick_lambda_proxy_invoke_handler);
+}
+
 // Return the address of quick stub code for resolving a method at first call.
 extern "C" void art_quick_resolution_trampoline(ArtMethod*);
 static inline const void* GetQuickResolutionStub() {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index da9a79e..07f0628 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -672,8 +672,8 @@
     return result;
   } else if (UNLIKELY(klass->IsPrimitive<kVerifyNone>())) {
     return Primitive::Descriptor(klass->GetPrimitiveType<kVerifyNone>());
-  } else if (UNLIKELY(klass->IsProxyClass<kVerifyNone>())) {
-    return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(klass);
+  } else if (UNLIKELY(klass->IsAnyProxyClass<kVerifyNone>())) {
+    return Runtime::Current()->GetClassLinker()->GetDescriptorForAnyProxy(klass);
   } else {
     mirror::DexCache* dex_cache = klass->GetDexCache<kVerifyNone>();
     if (!IsValidContinuousSpaceObjectAddress(dex_cache)) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1fe9a03..e2b2431 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -58,10 +58,7 @@
   CHECK_ALIGNED(max_delta, kPageSize);
   CHECK_LT(min_delta, max_delta);
 
-  std::default_random_engine generator;
-  generator.seed(NanoTime() * getpid());
-  std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
-  int32_t r = distribution(generator);
+  int32_t r = GetRandomNumber<int32_t>(min_delta, max_delta);
   if (r % 2 == 0) {
     r = RoundUp(r, kPageSize);
   } else {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 9f6699f..2de8e7e 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -888,12 +888,56 @@
     return false;
   }
 
+  StackHandleScope<1> hs{self};  // NOLINT: [readability/braces] [4];
+
+  // Use the lambda method's class loader since it's close enough.
+  // TODO: create-lambda should capture the current method's class loader and use that instead.
+  // TODO: Do we want create-lambda to work for static methods outside of the declaring class?
+  // --> then we need to store a classloader in the lambda method. otherwise we don't
+  //     because it would always use the declaring class's class loader.
+  // TODO: add a GetClassLoader to the lambda closure which knows how to do this,
+  //       don't hardcode this here.
+  Handle<ClassLoader> current_class_loader = hs.NewHandle(
+      lambda_closure->GetTargetMethod()->GetDeclaringClass()->GetClassLoader());
+
+  // TODO: get the type ID from the instruction
+  std::string class_name;
+  {
+    // Temporary hack to read the interface corresponding to a box-lambda.
+    // TODO: The box-lambda should encode the type ID instead, so we don't need to do this.
+    {
+      // Do a hack where we read from const-string the interface name
+      mirror::Object* string_reference = shadow_frame.GetVRegReference(vreg_target_object);
+
+      CHECK(string_reference != nullptr)
+          << "box-lambda needs the type name stored in string vA (target), but it was null";
+
+      CHECK(string_reference->IsString())
+          << "box-lambda needs the type name stored in string vA (target)";
+
+      mirror::String* as_string = string_reference->AsString();
+      class_name = as_string->ToModifiedUtf8();
+    }
+
+    // Trigger class loading of the functional interface.
+    // TODO: This should actually be done by the create-lambda...
+    if (Runtime::Current()->GetClassLinker()
+            ->FindClass(self, class_name.c_str(), current_class_loader) == nullptr) {
+      CHECK(self->IsExceptionPending());
+      self->AssertPendingException();
+      return false;
+    }
+  }
+
   mirror::Object* closure_as_object =
-      Runtime::Current()->GetLambdaBoxTable()->BoxLambda(lambda_closure);
+      Runtime::Current()->GetLambdaBoxTable()->BoxLambda(lambda_closure,
+                                                         class_name.c_str(),
+                                                         current_class_loader.Get());
 
   // Failed to box the lambda, an exception was raised.
   if (UNLIKELY(closure_as_object == nullptr)) {
     CHECK(self->IsExceptionPending());
+    shadow_frame.SetVRegReference(vreg_target_object, nullptr);
     return false;
   }
 
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index bf95a0e..11a8c2e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -102,6 +102,8 @@
   size_t lambda_captured_variable_index = 0;
   while (true) {
     dex_pc = inst->GetDexPc(insns);
+    DCHECK_LE(dex_pc, code_item->insns_size_in_code_units_)
+        << "Dex PC overflowed code item size; missing return instruction?";
     shadow_frame.SetDexPC(dex_pc);
     TraceExecution(shadow_frame, inst, dex_pc);
     inst_data = inst->Fetch16(0);
diff --git a/runtime/lambda/art_lambda_method.cc b/runtime/lambda/art_lambda_method.cc
index 6f9f8bb..0690cd1 100644
--- a/runtime/lambda/art_lambda_method.cc
+++ b/runtime/lambda/art_lambda_method.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "art_method-inl.h"
 #include "lambda/art_lambda_method.h"
 
 #include "base/logging.h"
@@ -73,5 +74,12 @@
   }
 }
 
+size_t ArtLambdaMethod::GetArgumentVRegCount() const {
+  DCHECK(GetArtMethod()->IsStatic());  // Instance methods don't have receiver in shorty.
+  const char* method_shorty = GetArtMethod()->GetShorty();
+  DCHECK_NE(*method_shorty, '\0') << method_shorty;
+  return ShortyFieldType::CountVirtualRegistersRequired(method_shorty + 1);  // skip return type
+}
+
 }  // namespace lambda
 }  // namespace art
diff --git a/runtime/lambda/art_lambda_method.h b/runtime/lambda/art_lambda_method.h
index ea13eb7..a858bf9 100644
--- a/runtime/lambda/art_lambda_method.h
+++ b/runtime/lambda/art_lambda_method.h
@@ -90,6 +90,17 @@
     return strlen(captured_variables_shorty_);
   }
 
+  // Return the offset in bytes from the start of ArtLambdaMethod to the method_.
+  // -- Only should be used by assembly (stubs) support code and compiled code.
+  static constexpr size_t GetArtMethodOffset() {
+    return offsetof(ArtLambdaMethod, method_);
+  }
+
+  // Calculate how many vregs all the arguments will use when doing an invoke.
+  // (Most primitives are 1 vregs, double/long are 2, reference is 1, lambda is 2).
+  // -- This is used to know how big to set up shadow frame when invoking into the target method.
+  size_t GetArgumentVRegCount() const SHARED_REQUIRES(Locks::mutator_lock_);
+
  private:
   // TODO: ArtMethod, or at least the entry points should be inlined into this struct
   // to avoid an extra indirect load when doing invokes.
diff --git a/runtime/lambda/box_class_table-inl.h b/runtime/lambda/box_class_table-inl.h
new file mode 100644
index 0000000..2fc34a7
--- /dev/null
+++ b/runtime/lambda/box_class_table-inl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_INL_H_
+#define ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_INL_H_
+
+#include "lambda/box_class_table.h"
+#include "thread.h"
+
+namespace art {
+namespace lambda {
+
+template <typename Visitor>
+inline void BoxClassTable::VisitRoots(const Visitor& visitor) {
+  MutexLock mu(Thread::Current(), *Locks::lambda_class_table_lock_);
+  for (std::pair<UnorderedMapKeyType, ValueType>& key_value : map_) {
+    ValueType& gc_root = key_value.second;
+    visitor.VisitRoot(gc_root.AddressWithoutBarrier());
+  }
+}
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_INL_H_
diff --git a/runtime/lambda/box_class_table.cc b/runtime/lambda/box_class_table.cc
new file mode 100644
index 0000000..1e49886
--- /dev/null
+++ b/runtime/lambda/box_class_table.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "lambda/box_class_table.h"
+
+#include "base/mutex.h"
+#include "common_throws.h"
+#include "gc_root-inl.h"
+#include "lambda/closure.h"
+#include "lambda/leaking_allocator.h"
+#include "mirror/method.h"
+#include "mirror/object-inl.h"
+#include "thread.h"
+
+#include <string>
+#include <vector>
+
+namespace art {
+namespace lambda {
+
+// Create the lambda proxy class given the name of the lambda interface (e.g. Ljava/lang/Runnable;)
+// Also needs a proper class loader (or null for bootclasspath) where the proxy will be created
+// into.
+//
+// The class must **not** have already been created.
+// Returns a non-null ptr on success, otherwise returns null and has an exception set.
+static mirror::Class* CreateClass(Thread* self,
+                                  const std::string& class_name,
+                                  const Handle<mirror::ClassLoader>& class_loader)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  ScopedObjectAccessUnchecked soa(self);
+  StackHandleScope<2> hs(self);
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+  // Find the java.lang.Class for our class name (from the class loader).
+  Handle<mirror::Class> lambda_interface =
+      hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), class_loader));
+  // TODO: use LookupClass in a loop
+  // TODO: DCHECK That this doesn't actually cause the class to be loaded,
+  //       since the create-lambda should've loaded it already
+  DCHECK(lambda_interface.Get() != nullptr) << "CreateClass with class_name=" << class_name;
+  DCHECK(lambda_interface->IsInterface()) << "CreateClass with class_name=" << class_name;
+  jobject lambda_interface_class = soa.AddLocalReference<jobject>(lambda_interface.Get());
+
+  // Look up java.lang.reflect.Proxy#getLambdaProxyClass method.
+  Handle<mirror::Class> java_lang_reflect_proxy =
+      hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/reflect/Proxy;"));
+  jclass java_lang_reflect_proxy_class =
+      soa.AddLocalReference<jclass>(java_lang_reflect_proxy.Get());
+  DCHECK(java_lang_reflect_proxy.Get() != nullptr);
+
+  jmethodID proxy_factory_method_id =
+      soa.Env()->GetStaticMethodID(java_lang_reflect_proxy_class,
+                                  "getLambdaProxyClass",
+                                  "(Ljava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;");
+  DCHECK(!soa.Env()->ExceptionCheck());
+
+  // Call into the java code to do the hard work of figuring out which methods and throws
+  // our lambda interface proxy needs to implement. It then calls back into the class linker
+  // on our behalf to make the proxy itself.
+  jobject generated_lambda_proxy_class =
+      soa.Env()->CallStaticObjectMethod(java_lang_reflect_proxy_class,
+                                        proxy_factory_method_id,
+                                        class_loader.ToJObject(),
+                                        lambda_interface_class);
+
+  // This can throw in which case we return null. Caller must handle.
+  return soa.Decode<mirror::Class*>(generated_lambda_proxy_class);
+}
+
+BoxClassTable::BoxClassTable() {
+}
+
+BoxClassTable::~BoxClassTable() {
+  // Don't need to do anything, classes are deleted automatically by GC
+  // when the classloader is deleted.
+  //
+  // Our table will not outlive the classloader since the classloader owns it.
+}
+
+mirror::Class* BoxClassTable::GetOrCreateBoxClass(const char* class_name,
+                                                  const Handle<mirror::ClassLoader>& class_loader) {
+  DCHECK(class_name != nullptr);
+
+  Thread* self = Thread::Current();
+
+  std::string class_name_str = class_name;
+
+  {
+    MutexLock mu(self, *Locks::lambda_class_table_lock_);
+
+    // Attempt to look up this class, it's possible it was already created previously.
+    // If this is the case we *must* return the same class as before to maintain
+    // referential equality between box instances.
+    //
+    // In managed code:
+    //   Functional f = () -> 5;  // vF = create-lambda
+    //   Object a = f;            // vA = box-lambda vA
+    //   Object b = f;            // vB = box-lambda vB
+    //   assert(a.getClass() == b.getClass())
+    //   assert(a == b)
+    ValueType value = FindBoxedClass(class_name_str);
+    if (!value.IsNull()) {
+      return value.Read();
+    }
+  }
+
+  // Otherwise we need to generate a class ourselves and insert it into the hash map
+
+  // Release the table lock here, which implicitly allows other threads to suspend
+  // (since the GC callbacks will not block on trying to acquire our lock).
+  // We also don't want to call into the class linker with the lock held because
+  // our lock level is lower.
+  self->AllowThreadSuspension();
+
+  // Create a lambda proxy class, within the specified class loader.
+  mirror::Class* lambda_proxy_class = CreateClass(self, class_name_str, class_loader);
+
+  // There are no thread suspension points after this, so we don't need to put it into a handle.
+  ScopedAssertNoThreadSuspension soants{self, "BoxClassTable::GetOrCreateBoxClass"};  // NOLINT:  [readability/braces] [4]
+
+  if (UNLIKELY(lambda_proxy_class == nullptr)) {
+    // Most likely an OOM has occurred.
+    CHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  {
+    MutexLock mu(self, *Locks::lambda_class_table_lock_);
+
+    // Possible, but unlikely, that someone already came in and made a proxy class
+    // on another thread.
+    ValueType value = FindBoxedClass(class_name_str);
+    if (UNLIKELY(!value.IsNull())) {
+      DCHECK_EQ(lambda_proxy_class, value.Read());
+      return value.Read();
+    }
+
+    // Otherwise we made a brand new proxy class.
+    // The class itself is cleaned up by the GC (e.g. class unloading) later.
+
+    // Actually insert into the table.
+    map_.Insert({std::move(class_name_str), ValueType(lambda_proxy_class)});
+  }
+
+  return lambda_proxy_class;
+}
+
+BoxClassTable::ValueType BoxClassTable::FindBoxedClass(const std::string& class_name) const {
+  auto map_iterator = map_.Find(class_name);
+  if (map_iterator != map_.end()) {
+    const std::pair<UnorderedMapKeyType, ValueType>& key_value_pair = *map_iterator;
+    const ValueType& value = key_value_pair.second;
+
+    DCHECK(!value.IsNull());  // Never store null boxes.
+    return value;
+  }
+
+  return ValueType(nullptr);
+}
+
+void BoxClassTable::EmptyFn::MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const {
+  item.first.clear();
+
+  Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+  item.second = ValueType();  // Also clear the GC root.
+}
+
+bool BoxClassTable::EmptyFn::IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const {
+  bool is_empty = item.first.empty();
+  DCHECK_EQ(item.second.IsNull(), is_empty);
+
+  return is_empty;
+}
+
+bool BoxClassTable::EqualsFn::operator()(const UnorderedMapKeyType& lhs,
+                                         const UnorderedMapKeyType& rhs) const {
+  // Be damn sure the classes don't just move around from under us.
+  Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+
+  // Being the same class name isn't enough, must also have the same class loader.
+  // When we are in the same class loader, classes are equal via the pointer.
+  return lhs == rhs;
+}
+
+size_t BoxClassTable::HashFn::operator()(const UnorderedMapKeyType& key) const {
+  return std::hash<std::string>()(key);
+}
+
+}  // namespace lambda
+}  // namespace art
diff --git a/runtime/lambda/box_class_table.h b/runtime/lambda/box_class_table.h
new file mode 100644
index 0000000..17e1026
--- /dev/null
+++ b/runtime/lambda/box_class_table.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_H_
+#define ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_H_
+
+#include "base/allocator.h"
+#include "base/hash_map.h"
+#include "gc_root.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "object_callbacks.h"
+
+#include <stdint.h>
+
+namespace art {
+
+class ArtMethod;  // forward declaration
+template<class T> class Handle;  // forward declaration
+
+namespace mirror {
+class Class;  // forward declaration
+class ClassLoader;  // forward declaration
+class LambdaProxy;  // forward declaration
+class Object;  // forward declaration
+}  // namespace mirror
+
+namespace lambda {
+struct Closure;  // forward declaration
+
+/*
+ * Store a table of boxed lambdas. This is required to maintain object referential equality
+ * when a lambda is re-boxed.
+ *
+ * Conceptually, we store a mapping of Class Name -> Weak Reference<Class>.
+ * When too many objects get GCd, we shrink the underlying table to use less space.
+ */
+class BoxClassTable FINAL {
+ public:
+  // TODO: This should take a LambdaArtMethod instead, read class name from that.
+  // Note: null class_loader means bootclasspath.
+  mirror::Class* GetOrCreateBoxClass(const char* class_name,
+                                     const Handle<mirror::ClassLoader>& class_loader)
+      REQUIRES(!Locks::lambda_class_table_lock_, !Roles::uninterruptible_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Sweep strong references to lambda class boxes. Update the addresses if the objects
+  // have been moved, and delete them from the table if the objects have been cleaned up.
+  template <typename Visitor>
+  void VisitRoots(const Visitor& visitor)
+      NO_THREAD_SAFETY_ANALYSIS  // for object marking requiring heap bitmap lock
+      REQUIRES(!Locks::lambda_class_table_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
+  BoxClassTable();
+  ~BoxClassTable();
+
+ private:
+  // We only store strong GC roots in our table.
+  using ValueType = GcRoot<mirror::Class>;
+
+  // Attempt to look up the class in the map, or return null if it's not there yet.
+  ValueType FindBoxedClass(const std::string& class_name) const
+      SHARED_REQUIRES(Locks::lambda_class_table_lock_);
+
+  // Store the key as a string so that we can have our own copy of the class name.
+  using UnorderedMapKeyType = std::string;
+
+  // EmptyFn implementation for art::HashMap
+  struct EmptyFn {
+    void MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const
+        NO_THREAD_SAFETY_ANALYSIS;
+        // SHARED_REQUIRES(Locks::mutator_lock_);
+
+    bool IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const;
+  };
+
+  // HashFn implementation for art::HashMap
+  struct HashFn {
+    size_t operator()(const UnorderedMapKeyType& key) const
+        NO_THREAD_SAFETY_ANALYSIS;
+        // SHARED_REQUIRES(Locks::mutator_lock_);
+  };
+
+  // EqualsFn implementation for art::HashMap
+  struct EqualsFn {
+    bool operator()(const UnorderedMapKeyType& lhs, const UnorderedMapKeyType& rhs) const
+        NO_THREAD_SAFETY_ANALYSIS;
+        // SHARED_REQUIRES(Locks::mutator_lock_);
+  };
+
+  using UnorderedMap = art::HashMap<UnorderedMapKeyType,
+                                    ValueType,
+                                    EmptyFn,
+                                    HashFn,
+                                    EqualsFn,
+                                    TrackingAllocator<std::pair<UnorderedMapKeyType, ValueType>,
+                                                      kAllocatorTagLambdaProxyClassBoxTable>>;
+
+  // Map of strong GC roots (lambda interface name -> lambda proxy class)
+  UnorderedMap map_ GUARDED_BY(Locks::lambda_class_table_lock_);
+
+  // Shrink the map when we get below this load factor.
+  // (This is an arbitrary value that should be large enough to prevent aggressive map erases
+  // from shrinking the table too often.)
+  static constexpr double kMinimumLoadFactor = UnorderedMap::kDefaultMinLoadFactor / 2;
+
+  DISALLOW_COPY_AND_ASSIGN(BoxClassTable);
+};
+
+}  // namespace lambda
+}  // namespace art
+
+#endif  // ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_H_
diff --git a/runtime/lambda/box_table.cc b/runtime/lambda/box_table.cc
index 9918bb7..0032d08 100644
--- a/runtime/lambda/box_table.cc
+++ b/runtime/lambda/box_table.cc
@@ -18,8 +18,10 @@
 #include "base/mutex.h"
 #include "common_throws.h"
 #include "gc_root-inl.h"
+#include "lambda/box_class_table.h"
 #include "lambda/closure.h"
 #include "lambda/leaking_allocator.h"
+#include "mirror/lambda_proxy.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "thread.h"
@@ -28,12 +30,13 @@
 
 namespace art {
 namespace lambda {
-// Temporarily represent the lambda Closure as its raw bytes in an array.
-// TODO: Generate a proxy class for the closure when boxing the first time.
-using BoxedClosurePointerType = mirror::ByteArray*;
+// All closures are boxed into a subtype of LambdaProxy which implements the lambda's interface.
+using BoxedClosurePointerType = mirror::LambdaProxy*;
 
-static mirror::Class* GetBoxedClosureClass() SHARED_REQUIRES(Locks::mutator_lock_) {
-  return mirror::ByteArray::GetArrayClass();
+// Returns the base class for all boxed closures.
+// Note that concrete closure boxes are actually a subtype of mirror::LambdaProxy.
+static mirror::Class* GetBoxedClosureBaseClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+  return Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangLambdaProxy);
 }
 
 namespace {
@@ -54,6 +57,14 @@
       return closure;
     }
   };
+
+  struct DeleterForClosure {
+    void operator()(Closure* closure) const {
+      ClosureAllocator::Delete(closure);
+    }
+  };
+
+  using UniqueClosurePtr = std::unique_ptr<Closure, DeleterForClosure>;
 }  // namespace
 
 BoxTable::BoxTable()
@@ -75,7 +86,9 @@
   }
 }
 
-mirror::Object* BoxTable::BoxLambda(const ClosureType& closure) {
+mirror::Object* BoxTable::BoxLambda(const ClosureType& closure,
+                                    const char* class_name,
+                                    mirror::ClassLoader* class_loader) {
   Thread* self = Thread::Current();
 
   {
@@ -91,7 +104,7 @@
     //   Functional f = () -> 5;  // vF = create-lambda
     //   Object a = f;            // vA = box-lambda vA
     //   Object b = f;            // vB = box-lambda vB
-    //   assert(a == f)
+    //   assert(a == b)
     ValueType value = FindBoxedLambda(closure);
     if (!value.IsNull()) {
       return value.Read();
@@ -100,30 +113,62 @@
     // Otherwise we need to box ourselves and insert it into the hash map
   }
 
-  // Release the lambda table lock here, so that thread suspension is allowed.
+  // Convert the Closure into a managed object instance, whose supertype of java.lang.LambdaProxy.
 
-  // Convert the Closure into a managed byte[] which will serve
-  // as the temporary 'boxed' version of the lambda. This is good enough
-  // to check all the basic object identities that a boxed lambda must retain.
-  // It's also good enough to contain all the captured primitive variables.
-
-  // TODO: Boxing an innate lambda (i.e. made with create-lambda) should make a proxy class
   // TODO: Boxing a learned lambda (i.e. made with unbox-lambda) should return the original object
-  BoxedClosurePointerType closure_as_array_object =
-      mirror::ByteArray::Alloc(self, closure->GetSize());
+  StackHandleScope<2> hs{self};  // NOLINT: [readability/braces] [4]
 
-  // There are no thread suspension points after this, so we don't need to put it into a handle.
+  Handle<mirror::ClassLoader> class_loader_handle = hs.NewHandle(class_loader);
 
-  if (UNLIKELY(closure_as_array_object == nullptr)) {
+  // Release the lambda table lock here, so that thread suspension is allowed.
+  self->AllowThreadSuspension();
+
+  lambda::BoxClassTable* lambda_box_class_table;
+
+  // Find the lambda box class table, which can be in the system class loader if classloader is null
+  if (class_loader == nullptr) {
+    ScopedObjectAccessUnchecked soa(self);
+    mirror::ClassLoader* system_class_loader =
+        soa.Decode<mirror::ClassLoader*>(Runtime::Current()->GetSystemClassLoader());
+    lambda_box_class_table = system_class_loader->GetLambdaProxyCache();
+  } else {
+    lambda_box_class_table = class_loader_handle->GetLambdaProxyCache();
+    // OK: can't be deleted while we hold a handle to the class loader.
+  }
+  DCHECK(lambda_box_class_table != nullptr);
+
+  Handle<mirror::Class> closure_class(hs.NewHandle(
+      lambda_box_class_table->GetOrCreateBoxClass(class_name, class_loader_handle)));
+  if (UNLIKELY(closure_class.Get() == nullptr)) {
     // Most likely an OOM has occurred.
-    CHECK(self->IsExceptionPending());
+    self->AssertPendingException();
     return nullptr;
   }
 
-  // Write the raw closure data into the byte[].
-  closure->CopyTo(closure_as_array_object->GetRawData(sizeof(uint8_t),  // component size
-                                                      0 /*index*/),     // index
-                  closure_as_array_object->GetLength());
+  BoxedClosurePointerType closure_as_object = nullptr;
+  UniqueClosurePtr closure_table_copy;
+  // Create an instance of the class, and assign the pointer to the closure into it.
+  {
+    closure_as_object = down_cast<BoxedClosurePointerType>(closure_class->AllocObject(self));
+    if (UNLIKELY(closure_as_object == nullptr)) {
+      self->AssertPendingOOMException();
+      return nullptr;
+    }
+
+    // Make a copy of the closure that we will store in the hash map.
+    // The proxy instance will also point to this same hash map.
+    // Note that the closure pointer is cleaned up only after the proxy is GCd.
+    closure_table_copy.reset(ClosureAllocator::Allocate(closure->GetSize()));
+    closure_as_object->SetClosure(closure_table_copy.get());
+  }
+
+  // There are no thread suspension points after this, so we don't need to put it into a handle.
+  ScopedAssertNoThreadSuspension soants{self,                                                    // NOLINT: [whitespace/braces] [5]
+                                        "box lambda table - box lambda - no more suspensions"};  // NOLINT: [whitespace/braces] [5]
+
+  // Write the raw closure data into the proxy instance's copy of the closure.
+  closure->CopyTo(closure_table_copy.get(),
+                  closure->GetSize());
 
   // The method has been successfully boxed into an object, now insert it into the hash map.
   {
@@ -134,24 +179,21 @@
     // we were allocating the object before.
     ValueType value = FindBoxedLambda(closure);
     if (UNLIKELY(!value.IsNull())) {
-      // Let the GC clean up method_as_object at a later time.
+      // Let the GC clean up closure_as_object at a later time.
+      // (We will not see this object when sweeping, it wasn't inserted yet.)
+      closure_as_object->SetClosure(nullptr);
       return value.Read();
     }
 
     // Otherwise we need to insert it into the hash map in this thread.
 
-    // Make a copy for the box table to keep, in case the closure gets collected from the stack.
-    // TODO: GC may need to sweep for roots in the box table's copy of the closure.
-    Closure* closure_table_copy = ClosureAllocator::Allocate(closure->GetSize());
-    closure->CopyTo(closure_table_copy, closure->GetSize());
-
-    // The closure_table_copy needs to be deleted by us manually when we erase it from the map.
+    // The closure_table_copy is deleted by us manually when we erase it from the map.
 
     // Actually insert into the table.
-    map_.Insert({closure_table_copy, ValueType(closure_as_array_object)});
+    map_.Insert({closure_table_copy.release(), ValueType(closure_as_object)});
   }
 
-  return closure_as_array_object;
+  return closure_as_object;
 }
 
 bool BoxTable::UnboxLambda(mirror::Object* object, ClosureType* out_closure) {
@@ -165,29 +207,35 @@
 
   mirror::Object* boxed_closure_object = object;
 
-  // Raise ClassCastException if object is not instanceof byte[]
-  if (UNLIKELY(!boxed_closure_object->InstanceOf(GetBoxedClosureClass()))) {
-    ThrowClassCastException(GetBoxedClosureClass(), boxed_closure_object->GetClass());
+  // Raise ClassCastException if object is not instanceof LambdaProxy
+  if (UNLIKELY(!boxed_closure_object->InstanceOf(GetBoxedClosureBaseClass()))) {
+    ThrowClassCastException(GetBoxedClosureBaseClass(), boxed_closure_object->GetClass());
     return false;
   }
 
   // TODO(iam): We must check that the closure object extends/implements the type
-  // specified in [type id]. This is not currently implemented since it's always a byte[].
+  // specified in [type id]. This is not currently implemented since the type id is unavailable.
 
   // If we got this far, the inputs are valid.
-  // Shuffle the byte[] back into a raw closure, then allocate it, copy, and return it.
-  BoxedClosurePointerType boxed_closure_as_array =
+  // Shuffle the java.lang.LambdaProxy back into a raw closure, then allocate it, copy,
+  // and return it.
+  BoxedClosurePointerType boxed_closure =
       down_cast<BoxedClosurePointerType>(boxed_closure_object);
 
-  const int8_t* unaligned_interior_closure = boxed_closure_as_array->GetData();
+  DCHECK_ALIGNED(boxed_closure->GetClosure(), alignof(Closure));
+  const Closure* aligned_interior_closure = boxed_closure->GetClosure();
+  DCHECK(aligned_interior_closure != nullptr);
+
+  // TODO: we probably don't need to make a copy here later on, once there's GC support.
 
   // Allocate a copy that can "escape" and copy the closure data into that.
   Closure* unboxed_closure =
-      LeakingAllocator::MakeFlexibleInstance<Closure>(self, boxed_closure_as_array->GetLength());
+      LeakingAllocator::MakeFlexibleInstance<Closure>(self, aligned_interior_closure->GetSize());
+  DCHECK_ALIGNED(unboxed_closure, alignof(Closure));
   // TODO: don't just memcpy the closure, it's unsafe when we add references to the mix.
-  memcpy(unboxed_closure, unaligned_interior_closure, boxed_closure_as_array->GetLength());
+  memcpy(unboxed_closure, aligned_interior_closure, aligned_interior_closure->GetSize());
 
-  DCHECK_EQ(unboxed_closure->GetSize(), static_cast<size_t>(boxed_closure_as_array->GetLength()));
+  DCHECK_EQ(unboxed_closure->GetSize(), aligned_interior_closure->GetSize());
 
   *out_closure = unboxed_closure;
   return true;
@@ -236,9 +284,10 @@
 
     if (new_value == nullptr) {
       // The object has been swept away.
-      const ClosureType& closure = key_value_pair.first;
+      Closure* closure = key_value_pair.first;
 
       // Delete the entry from the map.
+      // (Remove from map first to avoid accessing dangling pointer).
       map_iterator = map_.Erase(map_iterator);
 
       // Clean up the memory by deleting the closure.
@@ -290,7 +339,10 @@
 }
 
 bool BoxTable::EmptyFn::IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const {
-  return item.first == nullptr;
+  bool is_empty = item.first == nullptr;
+  DCHECK_EQ(item.second.IsNull(), is_empty);
+
+  return is_empty;
 }
 
 bool BoxTable::EqualsFn::operator()(const UnorderedMapKeyType& lhs,
diff --git a/runtime/lambda/box_table.h b/runtime/lambda/box_table.h
index adb7332..9dca6ab 100644
--- a/runtime/lambda/box_table.h
+++ b/runtime/lambda/box_table.h
@@ -30,6 +30,9 @@
 class ArtMethod;  // forward declaration
 
 namespace mirror {
+class Class;   // forward declaration
+class ClassLoader;  // forward declaration
+class LambdaProxy;  // forward declaration
 class Object;  // forward declaration
 }  // namespace mirror
 
@@ -48,8 +51,11 @@
   using ClosureType = art::lambda::Closure*;
 
   // Boxes a closure into an object. Returns null and throws an exception on failure.
-  mirror::Object* BoxLambda(const ClosureType& closure)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::lambda_table_lock_);
+  mirror::Object* BoxLambda(const ClosureType& closure,
+                            const char* class_name,
+                            mirror::ClassLoader* class_loader)
+      REQUIRES(!Locks::lambda_table_lock_, !Roles::uninterruptible_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Unboxes an object back into the lambda. Returns false and throws an exception on failure.
   bool UnboxLambda(mirror::Object* object, ClosureType* out_closure)
@@ -128,7 +134,16 @@
                                     TrackingAllocator<std::pair<ClosureType, ValueType>,
                                                       kAllocatorTagLambdaBoxTable>>;
 
+  using ClassMap = art::HashMap<std::string,
+                                GcRoot<mirror::Class>,
+                                EmptyFn,
+                                HashFn,
+                                EqualsFn,
+                                TrackingAllocator<std::pair<ClosureType, ValueType>,
+                                                      kAllocatorTagLambdaProxyClassBoxTable>>;
+
   UnorderedMap map_                                          GUARDED_BY(Locks::lambda_table_lock_);
+  UnorderedMap classes_map_                                  GUARDED_BY(Locks::lambda_table_lock_);
   bool allow_new_weaks_                                      GUARDED_BY(Locks::lambda_table_lock_);
   ConditionVariable new_weaks_condition_                     GUARDED_BY(Locks::lambda_table_lock_);
 
diff --git a/runtime/lambda/closure.cc b/runtime/lambda/closure.cc
index 179e4ee..f935e04 100644
--- a/runtime/lambda/closure.cc
+++ b/runtime/lambda/closure.cc
@@ -20,9 +20,6 @@
 #include "lambda/art_lambda_method.h"
 #include "runtime/mirror/object_reference.h"
 
-static constexpr const bool kClosureSupportsReferences = false;
-static constexpr const bool kClosureSupportsGarbageCollection = false;
-
 namespace art {
 namespace lambda {
 
@@ -128,6 +125,10 @@
   return const_cast<ArtMethod*>(lambda_info_->GetArtMethod());
 }
 
+ArtLambdaMethod* Closure::GetLambdaInfo() const {
+  return const_cast<ArtLambdaMethod*>(lambda_info_);
+}
+
 uint32_t Closure::GetHashCode() const {
   // Start with a non-zero constant, a prime number.
   uint32_t result = 17;
diff --git a/runtime/lambda/closure.h b/runtime/lambda/closure.h
index 31ff194..38ec063 100644
--- a/runtime/lambda/closure.h
+++ b/runtime/lambda/closure.h
@@ -33,12 +33,52 @@
 class ArtLambdaMethod;  // forward declaration
 class ClosureBuilder;   // forward declaration
 
+// TODO: Remove these constants once closures are supported properly.
+
+// Does the lambda closure support containing references? If so, all the users of lambdas
+// must be updated to also support references.
+static constexpr const bool kClosureSupportsReferences = false;
+// Does the lambda closure support being garbage collected? If so, all the users of lambdas
+// must be updated to also support garbage collection.
+static constexpr const bool kClosureSupportsGarbageCollection = false;
+// Does the lambda closure support being garbage collected with a read barrier? If so,
+// all the users of the lambdas msut also be updated to support read barrier GC.
+static constexpr const bool kClosureSupportsReadBarrier = false;
+
+// Is this closure being stored as a 'long' in shadow frames and the quick ABI?
+static constexpr const bool kClosureIsStoredAsLong = true;
+
+
+// Raw memory layout for the lambda closure.
+//
+// WARNING:
+// * This should only be used by the compiler and tests, as they need to offsetof the raw fields.
+// * Runtime/interpreter should always access closures through a Closure pointer.
+struct ClosureStorage {
+  // Compile-time known lambda information such as the type descriptor and size.
+  ArtLambdaMethod* lambda_info_;
+
+  // A contiguous list of captured variables, and possibly the closure size.
+  // The runtime size can always be determined through GetSize().
+  union {
+    // Read from here if the closure size is static (ArtLambdaMethod::IsStatic)
+    uint8_t static_variables_[0];
+    struct {
+      // Read from here if the closure size is dynamic (ArtLambdaMethod::IsDynamic)
+      size_t size_;  // The lambda_info_ and the size_ itself is also included as part of the size.
+      uint8_t variables_[0];
+    } dynamic_;
+  } captured_[0];
+  // captured_ will always consist of one array element at runtime.
+  // Set to [0] so that 'size_' is not counted in sizeof(Closure).
+};
+
 // Inline representation of a lambda closure.
 // Contains the target method and the set of packed captured variables as a copy.
 //
 // The closure itself is logically immutable, although in practice any object references
 // it (recursively) contains can be moved and updated by the GC.
-struct PACKED(sizeof(ArtLambdaMethod*)) Closure {
+struct Closure : private ClosureStorage {
   // Get the size of the Closure in bytes.
   // This is necessary in order to allocate a large enough area to copy the Closure into.
   // Do *not* copy the closure with memcpy, since references also need to get moved.
@@ -52,6 +92,9 @@
   // Get the target method, i.e. the method that will be dispatched into with invoke-lambda.
   ArtMethod* GetTargetMethod() const;
 
+  // Get the static lambda info that never changes.
+  ArtLambdaMethod* GetLambdaInfo() const;
+
   // Calculates the hash code. Value is recomputed each time.
   uint32_t GetHashCode() const SHARED_REQUIRES(Locks::mutator_lock_);
 
@@ -156,28 +199,15 @@
   static size_t GetClosureSize(const uint8_t* closure);
 
   ///////////////////////////////////////////////////////////////////////////////////
-
-  // Compile-time known lambda information such as the type descriptor and size.
-  ArtLambdaMethod* lambda_info_;
-
-  // A contiguous list of captured variables, and possibly the closure size.
-  // The runtime size can always be determined through GetSize().
-  union {
-    // Read from here if the closure size is static (ArtLambdaMethod::IsStatic)
-    uint8_t static_variables_[0];
-    struct {
-      // Read from here if the closure size is dynamic (ArtLambdaMethod::IsDynamic)
-      size_t size_;  // The lambda_info_ and the size_ itself is also included as part of the size.
-      uint8_t variables_[0];
-    } dynamic_;
-  } captured_[0];
-  // captured_ will always consist of one array element at runtime.
-  // Set to [0] so that 'size_' is not counted in sizeof(Closure).
-
-  friend class ClosureBuilder;
+  // NOTE: Actual fields are declared in ClosureStorage.
   friend class ClosureTest;
 };
 
+// ABI guarantees:
+// * Closure same size as a ClosureStorage
+// * ClosureStorage begins at the same point a Closure would begin.
+static_assert(sizeof(Closure) == sizeof(ClosureStorage), "Closure size must match ClosureStorage");
+
 }  // namespace lambda
 }  // namespace art
 
diff --git a/runtime/lambda/closure_builder.cc b/runtime/lambda/closure_builder.cc
index 739e965..7b36042 100644
--- a/runtime/lambda/closure_builder.cc
+++ b/runtime/lambda/closure_builder.cc
@@ -75,7 +75,7 @@
   if (LIKELY(is_dynamic_size_ == false)) {
     // Write in the extra bytes to store the dynamic size the first time.
     is_dynamic_size_ = true;
-    size_ += sizeof(Closure::captured_[0].dynamic_.size_);
+    size_ += sizeof(ClosureStorage::captured_[0].dynamic_.size_);
   }
 
   // A closure may be sized dynamically, so always query it for the true size.
@@ -107,38 +107,40 @@
     << "number of variables captured at runtime does not match "
     << "number of variables captured at compile time";
 
-  Closure* closure = new (memory) Closure;
-  closure->lambda_info_ = target_method;
+  ClosureStorage* closure_storage = new (memory) ClosureStorage;
+  closure_storage->lambda_info_ = target_method;
 
-  static_assert(offsetof(Closure, captured_) == kInitialSize, "wrong initial size");
+  static_assert(offsetof(ClosureStorage, captured_) == kInitialSize, "wrong initial size");
 
   size_t written_size;
   if (UNLIKELY(is_dynamic_size_)) {
     // The closure size must be set dynamically (i.e. nested lambdas).
-    closure->captured_[0].dynamic_.size_ = GetSize();
-    size_t header_size = offsetof(Closure, captured_[0].dynamic_.variables_);
+    closure_storage->captured_[0].dynamic_.size_ = GetSize();
+    size_t header_size = offsetof(ClosureStorage, captured_[0].dynamic_.variables_);
     DCHECK_LE(header_size, GetSize());
     size_t variables_size = GetSize() - header_size;
     written_size =
         WriteValues(target_method,
-                    closure->captured_[0].dynamic_.variables_,
+                    closure_storage->captured_[0].dynamic_.variables_,
                     header_size,
                     variables_size);
   } else {
     // The closure size is known statically (i.e. no nested lambdas).
     DCHECK(GetSize() == target_method->GetStaticClosureSize());
-    size_t header_size = offsetof(Closure, captured_[0].static_variables_);
+    size_t header_size = offsetof(ClosureStorage, captured_[0].static_variables_);
     DCHECK_LE(header_size, GetSize());
     size_t variables_size = GetSize() - header_size;
     written_size =
         WriteValues(target_method,
-                    closure->captured_[0].static_variables_,
+                    closure_storage->captured_[0].static_variables_,
                     header_size,
                     variables_size);
   }
 
-  DCHECK_EQ(written_size, closure->GetSize());
+  // OK: The closure storage is guaranteed to be the same as a closure.
+  Closure* closure = reinterpret_cast<Closure*>(closure_storage);
 
+  DCHECK_EQ(written_size, closure->GetSize());
   return closure;
 }
 
diff --git a/runtime/lambda/shorty_field_type.h b/runtime/lambda/shorty_field_type.h
index 46ddaa9..54bb4d4 100644
--- a/runtime/lambda/shorty_field_type.h
+++ b/runtime/lambda/shorty_field_type.h
@@ -285,6 +285,39 @@
     }
   }
 
+  // Get the number of virtual registers necessary to represent this type as a stack local.
+  inline size_t GetVirtualRegisterCount() const {
+    if (IsPrimitiveNarrow()) {
+      return 1;
+    } else if (IsPrimitiveWide()) {
+      return 2;
+    } else if (IsObject()) {
+      return kObjectReferenceSize / sizeof(uint32_t);
+    } else if (IsLambda()) {
+      return 2;
+    } else {
+      DCHECK(false) << "unknown shorty field type '" << static_cast<char>(value_) << "'";
+      UNREACHABLE();
+    }
+  }
+
+  // Count how many virtual registers would be necessary in order to store this list of shorty
+  // field types.
+  inline size_t static CountVirtualRegistersRequired(const char* shorty) {
+    size_t size = 0;
+
+    while (shorty != nullptr && *shorty != '\0') {
+      // Each argument appends to the size.
+      ShortyFieldType shorty_field{*shorty};  // NOLINT [readability/braces] [4]
+
+      size += shorty_field.GetVirtualRegisterCount();
+
+      ++shorty;
+  }
+
+    return size;
+  }
+
   // Implicitly convert to the anonymous nested inner type. Used for exhaustive switch detection.
   inline operator decltype(kByte)() const {
     return value_;
diff --git a/runtime/lambda/shorty_field_type_test.cc b/runtime/lambda/shorty_field_type_test.cc
index 32bade9..430e39e 100644
--- a/runtime/lambda/shorty_field_type_test.cc
+++ b/runtime/lambda/shorty_field_type_test.cc
@@ -218,6 +218,56 @@
   }
 }  // TEST_F
 
+TEST_F(ShortyFieldTypeTest, TestCalculateVRegSize) {
+  // Make sure the single calculation for each value is correct.
+  std::pair<size_t, char> expected_actual_single[] = {
+      // Primitives
+      { 1u, 'Z' },
+      { 1u, 'B' },
+      { 1u, 'C' },
+      { 1u, 'S' },
+      { 1u, 'I' },
+      { 1u, 'F' },
+      { 2u, 'J' },
+      { 2u, 'D' },
+      // Non-primitives
+      { 1u, 'L' },
+      { 2u, '\\' },
+  };
+
+  for (auto pair : expected_actual_single) {
+    SCOPED_TRACE(pair.second);
+    EXPECT_EQ(pair.first, ShortyFieldType(pair.second).GetVirtualRegisterCount());
+  }
+
+  // Make sure we are correctly calculating how many virtual registers a shorty descriptor takes.
+  std::pair<size_t, const char*> expected_actual[] = {
+      // Empty list
+      { 0u, "" },
+      // Primitives
+      { 1u, "Z" },
+      { 1u, "B" },
+      { 1u, "C" },
+      { 1u, "S" },
+      { 1u, "I" },
+      { 1u, "F" },
+      { 2u, "J" },
+      { 2u, "D" },
+      // Non-primitives
+      { 1u, "L" },
+      { 2u, "\\" },
+      // Multiple things at once:
+      { 10u, "ZBCSIFJD" },
+      { 5u, "LLSSI" },
+      { 6u, "LLL\\L" }
+  };
+
+  for (auto pair : expected_actual) {
+    SCOPED_TRACE(pair.second);
+    EXPECT_EQ(pair.first, ShortyFieldType::CountVirtualRegistersRequired(pair.second));
+  }
+}  // TEST_F
+
 // Helper class to probe a shorty's characteristics by minimizing copy-and-paste tests.
 template <typename T, decltype(ShortyFieldType::kByte) kShortyEnum>
 struct ShortyTypeCharacteristics {
diff --git a/runtime/lambda_proxy_test.cc b/runtime/lambda_proxy_test.cc
new file mode 100644
index 0000000..63d6ccc
--- /dev/null
+++ b/runtime/lambda_proxy_test.cc
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <vector>
+
+#include "art_field-inl.h"
+#include "class_linker-inl.h"
+#include "compiler_callbacks.h"
+#include "common_compiler_test.h"
+#include "mirror/field-inl.h"
+#include "mirror/lambda_proxy.h"
+#include "mirror/method.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+
+// The enclosing class of all the interfaces used by this test.
+// -- Defined as a macro to allow for string concatenation.
+#define TEST_INTERFACE_ENCLOSING_CLASS_NAME "LambdaInterfaces"
+// Generate out "LLambdaInterfaces$<<iface>>;" , replacing <<iface>> with the interface name.
+#define MAKE_TEST_INTERFACE_NAME(iface) ("L" TEST_INTERFACE_ENCLOSING_CLASS_NAME "$" iface ";")
+
+#define ASSERT_NOT_NULL(x) ASSERT_TRUE((x) != nullptr)
+#define ASSERT_NULL(x) ASSERT_TRUE((x) == nullptr)
+#define EXPECT_NULL(x) EXPECT_TRUE((x) == nullptr)
+
+class LambdaProxyTest  // : public CommonCompilerTest {
+    : public CommonRuntimeTest {
+ public:
+  // Generate a lambda proxy class with the given name and interfaces. This is a simplification from what
+  // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and
+  // we do not declare exceptions.
+  mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa,
+                                    jobject jclass_loader,
+                                    const char* class_name,
+                                    const std::vector<mirror::Class*>& interfaces)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    CHECK(class_name != nullptr);
+    CHECK(jclass_loader != nullptr);
+
+    mirror::Class* java_lang_object =
+        class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+    CHECK(java_lang_object != nullptr);
+
+    jclass java_lang_class = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass());
+
+    // Builds the interfaces array.
+    jobjectArray proxy_class_interfaces = soa.Env()->NewObjectArray(interfaces.size(),
+                                                                    java_lang_class,
+                                                                    nullptr);  // No initial element.
+    soa.Self()->AssertNoPendingException();
+    for (size_t i = 0; i < interfaces.size(); ++i) {
+      soa.Env()->SetObjectArrayElement(proxy_class_interfaces,
+                                       i,
+                                       soa.AddLocalReference<jclass>(interfaces[i]));
+    }
+
+    // Builds the method array.
+    jsize methods_count = 3;  // Object.equals, Object.hashCode and Object.toString.
+    for (mirror::Class* interface : interfaces) {
+      methods_count += interface->NumVirtualMethods();
+    }
+    jobjectArray proxy_class_methods =
+        soa.Env()->NewObjectArray(methods_count,
+                                  soa.AddLocalReference<jclass>(mirror::Method::StaticClass()),
+                                  nullptr);  // No initial element.
+    soa.Self()->AssertNoPendingException();
+
+    jsize array_index = 0;
+
+    //
+    // Fill the method array with the Object and all the interface's virtual methods.
+    //
+
+    // Add a method to 'proxy_class_methods'
+    auto add_method_to_array = [&](ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
+      CHECK(method != nullptr);
+      soa.Env()->SetObjectArrayElement(proxy_class_methods,
+                                       array_index++,
+                                       soa.AddLocalReference<jobject>(
+                                           mirror::Method::CreateFromArtMethod(soa.Self(),
+                                                                               method))
+                                      );  // NOLINT: [whitespace/parens] [2]
+
+      LOG(DEBUG) << "Add " << PrettyMethod(method) << " to list of methods to generate proxy";
+    };
+    // Add a method to 'proxy_class_methods' by looking it up from java.lang.Object
+    auto add_method_to_array_by_lookup = [&](const char* name, const char* method_descriptor)
+        SHARED_REQUIRES(Locks::mutator_lock_) {
+      ArtMethod* method = java_lang_object->FindDeclaredVirtualMethod(name,
+                                                                      method_descriptor,
+                                                                      sizeof(void*));
+      add_method_to_array(method);
+    };
+
+    // Add all methods from Object.
+    add_method_to_array_by_lookup("equals",   "(Ljava/lang/Object;)Z");
+    add_method_to_array_by_lookup("hashCode", "()I");
+    add_method_to_array_by_lookup("toString", "()Ljava/lang/String;");
+
+    // Now adds all interfaces virtual methods.
+    for (mirror::Class* interface : interfaces) {
+      mirror::Class* next_class = interface;
+      do {
+        for (ArtMethod& method : next_class->GetVirtualMethods(sizeof(void*))) {
+          add_method_to_array(&method);
+        }
+        next_class = next_class->GetSuperClass();
+      } while (!next_class->IsObjectClass());
+      // Skip adding any methods from "Object".
+    }
+    CHECK_EQ(array_index, methods_count);
+
+    // Builds an empty exception array.
+    jobjectArray proxy_class_throws = soa.Env()->NewObjectArray(0 /* length */,
+                                                                java_lang_class,
+                                                                nullptr /* initial element*/);
+    soa.Self()->AssertNoPendingException();
+
+    bool already_exists;
+    mirror::Class* proxy_class =
+        class_linker_->CreateLambdaProxyClass(soa,
+                                              soa.Env()->NewStringUTF(class_name),
+                                              proxy_class_interfaces,
+                                              jclass_loader,
+                                              proxy_class_methods,
+                                              proxy_class_throws,
+                                              /*out*/&already_exists);
+
+    CHECK(!already_exists);
+
+    soa.Self()->AssertNoPendingException();
+    return proxy_class;
+  }
+
+  LambdaProxyTest() {
+  }
+
+  virtual void SetUp() {
+    CommonRuntimeTest::SetUp();
+  }
+
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {
+    // Do not have any compiler options because we don't want to run as an AOT
+    // (In particular the lambda proxy class generation isn't currently supported for AOT).
+    this->callbacks_.reset();
+  }
+
+  template <typename THandleScope>
+  Handle<mirror::Class> GenerateProxyClass(THandleScope& hs,
+                                           const char* name,
+                                           const std::vector<mirror::Class*>& interfaces)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    return hs.NewHandle(GenerateProxyClass(*soa_, jclass_loader_, name, interfaces));
+  }
+
+ protected:
+  ScopedObjectAccess* soa_ = nullptr;
+  jobject jclass_loader_ = nullptr;
+};
+
+// Creates a lambda proxy class and check ClassHelper works correctly.
+TEST_F(LambdaProxyTest, ProxyClassHelper) {
+  // gLogVerbosity.class_linker = true;  // Uncomment to enable class linker logging.
+
+  ASSERT_NOT_NULL(Thread::Current());
+
+  ScopedObjectAccess soa(Thread::Current());
+  soa_ = &soa;
+
+  // Must happen after CommonRuntimeTest finishes constructing the runtime.
+  jclass_loader_ = LoadDex(TEST_INTERFACE_ENCLOSING_CLASS_NAME);
+  jobject jclass_loader = jclass_loader_;
+
+  StackHandleScope<4> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+  Handle<mirror::Class> J(hs.NewHandle(
+      class_linker_->FindClass(soa.Self(), MAKE_TEST_INTERFACE_NAME("J"), class_loader)));
+  ASSERT_TRUE(J.Get() != nullptr);
+
+  std::vector<mirror::Class*> interfaces;
+  interfaces.push_back(J.Get());
+  Handle<mirror::Class> proxy_class(hs.NewHandle(
+      GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
+  interfaces.clear();  // Don't least possibly stale objects in the array as good practice.
+  ASSERT_TRUE(proxy_class.Get() != nullptr);
+  ASSERT_TRUE(proxy_class->IsLambdaProxyClass());
+  ASSERT_TRUE(proxy_class->IsInitialized());
+
+  EXPECT_EQ(1U, proxy_class->NumDirectInterfaces());  // LambdaInterfaces$J.
+  EXPECT_EQ(J.Get(), mirror::Class::GetDirectInterface(soa.Self(), proxy_class, 0));
+  std::string temp;
+  const char* proxy_class_descriptor = proxy_class->GetDescriptor(&temp);
+  EXPECT_STREQ("L$Proxy1234;", proxy_class_descriptor);
+  EXPECT_EQ(nullptr, proxy_class->GetSourceFile());
+
+  // Make sure all the virtual methods are marked as a proxy
+  for (ArtMethod& method : proxy_class->GetVirtualMethods(sizeof(void*))) {
+    SCOPED_TRACE(PrettyMethod(&method, /* with_signature */true));
+    EXPECT_TRUE(method.IsProxyMethod());
+    EXPECT_TRUE(method.IsLambdaProxyMethod());
+    EXPECT_FALSE(method.IsReflectProxyMethod());
+  }
+}
+
+// Creates a proxy class and check FieldHelper works correctly.
+TEST_F(LambdaProxyTest, ProxyFieldHelper) {
+  // gLogVerbosity.class_linker = true;  // Uncomment to enable class linker logging.
+
+  ASSERT_NOT_NULL(Thread::Current());
+
+  ScopedObjectAccess soa(Thread::Current());
+  soa_ = &soa;
+
+  // Must happen after CommonRuntimeTest finishes constructing the runtime.
+  jclass_loader_ = LoadDex(TEST_INTERFACE_ENCLOSING_CLASS_NAME);
+  jobject jclass_loader = jclass_loader_;
+
+  StackHandleScope<9> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+  Handle<mirror::Class> I(hs.NewHandle(
+      class_linker_->FindClass(soa.Self(), MAKE_TEST_INTERFACE_NAME("I"), class_loader)));
+  ASSERT_NOT_NULL(I.Get());
+
+  // Create the lambda proxy which implements interfaces "I".
+  Handle<mirror::Class> proxy_class = GenerateProxyClass(hs,
+                                                         "$Proxy1234",
+                                                         { I.Get() });  // Interfaces.
+
+  ASSERT_NOT_NULL(proxy_class.Get());
+  EXPECT_TRUE(proxy_class->IsLambdaProxyClass());
+  EXPECT_TRUE(proxy_class->IsInitialized());
+  EXPECT_NULL(proxy_class->GetIFieldsPtr());
+
+  LengthPrefixedArray<ArtField>* static_fields = proxy_class->GetSFieldsPtr();
+  ASSERT_NOT_NULL(static_fields);
+
+  // Must have "throws" and "interfaces" static fields.
+  ASSERT_EQ(+mirror::LambdaProxy::kStaticFieldCount, proxy_class->NumStaticFields());
+
+  static constexpr const char* kInterfacesClassName = "[Ljava/lang/Class;";
+  static constexpr const char* kThrowsClassName     = "[[Ljava/lang/Class;";
+
+  // Class for "interfaces" field.
+  Handle<mirror::Class> interfaces_field_class =
+      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), kInterfacesClassName));
+  ASSERT_NOT_NULL(interfaces_field_class.Get());
+
+  // Class for "throws" field.
+  Handle<mirror::Class> throws_field_class =
+      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), kThrowsClassName));
+  ASSERT_NOT_NULL(throws_field_class.Get());
+
+  // Helper to test the static fields for correctness.
+  auto test_static_field = [&](size_t index,
+                               const char* field_name,
+                               Handle<mirror::Class>& handle_class,
+                               const char* class_name)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    ArtField* field = &static_fields->At(index);
+    EXPECT_STREQ(field_name, field->GetName());
+    EXPECT_STREQ(class_name, field->GetTypeDescriptor());
+    EXPECT_EQ(handle_class.Get(), field->GetType</*kResolve*/true>())
+        << "Expected: " << PrettyClass(interfaces_field_class.Get()) << ", "
+        << "Actual: " << PrettyClass(field->GetType</*kResolve*/true>()) << ", "
+        << "field_name: " << field_name;
+    std::string temp;
+    EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp));
+    EXPECT_FALSE(field->IsPrimitiveType());
+  };
+
+  // Test "Class[] interfaces" field.
+  test_static_field(mirror::LambdaProxy::kStaticFieldIndexInterfaces,
+                    "interfaces",
+                    interfaces_field_class,
+                    kInterfacesClassName);
+
+  // Test "Class[][] throws" field.
+  test_static_field(mirror::LambdaProxy::kStaticFieldIndexThrows,
+                    "throws",
+                    throws_field_class,
+                    kThrowsClassName);
+}
+
+// Creates two proxy classes and check the art/mirror fields of their static fields.
+TEST_F(LambdaProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) {
+  // gLogVerbosity.class_linker = true;  // Uncomment to enable class linker logging.
+
+  ASSERT_NOT_NULL(Thread::Current());
+
+  ScopedObjectAccess soa(Thread::Current());
+  soa_ = &soa;
+
+  // Must happen after CommonRuntimeTest finishes constructing the runtime.
+  jclass_loader_ = LoadDex(TEST_INTERFACE_ENCLOSING_CLASS_NAME);
+  jobject jclass_loader = jclass_loader_;
+
+  StackHandleScope<8> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+  Handle<mirror::Class> proxyClass0;
+  Handle<mirror::Class> proxyClass1;
+  {
+    Handle<mirror::Class> L(hs.NewHandle(
+        class_linker_->FindClass(soa.Self(), MAKE_TEST_INTERFACE_NAME("L"), class_loader)));
+    ASSERT_TRUE(L.Get() != nullptr);
+
+    std::vector<mirror::Class*> interfaces = { L.Get() };
+    proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces));
+    proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
+  }
+
+  ASSERT_TRUE(proxyClass0.Get() != nullptr);
+  ASSERT_TRUE(proxyClass0->IsLambdaProxyClass());
+  ASSERT_TRUE(proxyClass0->IsInitialized());
+  ASSERT_TRUE(proxyClass1.Get() != nullptr);
+  ASSERT_TRUE(proxyClass1->IsLambdaProxyClass());
+  ASSERT_TRUE(proxyClass1->IsInitialized());
+
+  LengthPrefixedArray<ArtField>* static_fields0 = proxyClass0->GetSFieldsPtr();
+  ASSERT_TRUE(static_fields0 != nullptr);
+  ASSERT_EQ(2u, static_fields0->size());
+  LengthPrefixedArray<ArtField>* static_fields1 = proxyClass1->GetSFieldsPtr();
+  ASSERT_TRUE(static_fields1 != nullptr);
+  ASSERT_EQ(2u, static_fields1->size());
+
+  EXPECT_EQ(static_fields0->At(0).GetDeclaringClass(), proxyClass0.Get());
+  EXPECT_EQ(static_fields0->At(1).GetDeclaringClass(), proxyClass0.Get());
+  EXPECT_EQ(static_fields1->At(0).GetDeclaringClass(), proxyClass1.Get());
+  EXPECT_EQ(static_fields1->At(1).GetDeclaringClass(), proxyClass1.Get());
+
+  Handle<mirror::Field> field00 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0->At(0), true));
+  Handle<mirror::Field> field01 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0->At(1), true));
+  Handle<mirror::Field> field10 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1->At(0), true));
+  Handle<mirror::Field> field11 =
+      hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1->At(1), true));
+  EXPECT_EQ(field00->GetArtField(), &static_fields0->At(0));
+  EXPECT_EQ(field01->GetArtField(), &static_fields0->At(1));
+  EXPECT_EQ(field10->GetArtField(), &static_fields1->At(0));
+  EXPECT_EQ(field11->GetArtField(), &static_fields1->At(1));
+}
+
+// TODO: make sure there's a non-abstract implementation of the single-abstract-method on the class.
+
+}  // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 9e416dc..a8685b8 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -695,7 +695,11 @@
 }
 
 inline const DexFile& Class::GetDexFile() {
-  return *GetDexCache()->GetDexFile();
+  DexCache* dex_cache = GetDexCache();
+  DCHECK(dex_cache != nullptr);
+  const DexFile* dex_file = dex_cache->GetDexFile();
+  DCHECK(dex_file != nullptr);
+  return *dex_file;
 }
 
 inline bool Class::DescriptorEquals(const char* match) {
@@ -703,8 +707,8 @@
     return match[0] == '[' && GetComponentType()->DescriptorEquals(match + 1);
   } else if (IsPrimitive()) {
     return strcmp(Primitive::Descriptor(GetPrimitiveType()), match) == 0;
-  } else if (IsProxyClass()) {
-    return ProxyDescriptorEquals(match);
+  } else if (IsAnyProxyClass()) {
+    return AnyProxyDescriptorEquals(match);
   } else {
     const DexFile& dex_file = GetDexFile();
     const DexFile::TypeId& type_id = dex_file.GetTypeId(GetClassDef()->class_idx_);
@@ -720,22 +724,32 @@
   }
 }
 
-inline ObjectArray<Class>* Class::GetInterfaces() {
-  CHECK(IsProxyClass());
+inline ObjectArray<Class>* Class::GetInterfacesForAnyProxy() {
+  CHECK(IsAnyProxyClass());
   // First static field.
   auto* field = GetStaticField(0);
   DCHECK_STREQ(field->GetName(), "interfaces");
   MemberOffset field_offset = field->GetOffset();
-  return GetFieldObject<ObjectArray<Class>>(field_offset);
+  ObjectArray<Class>* interfaces_array = GetFieldObject<ObjectArray<Class>>(field_offset);
+
+  CHECK(interfaces_array != nullptr);
+  if (UNLIKELY(IsLambdaProxyClass())) {
+    DCHECK_EQ(1, interfaces_array->GetLength())
+        << "Lambda proxies cannot have multiple direct interfaces implemented";
+  }
+  return interfaces_array;
 }
 
-inline ObjectArray<ObjectArray<Class>>* Class::GetThrows() {
-  CHECK(IsProxyClass());
+inline ObjectArray<ObjectArray<Class>>* Class::GetThrowsForAnyProxy() {
+  CHECK(IsAnyProxyClass());
   // Second static field.
   auto* field = GetStaticField(1);
   DCHECK_STREQ(field->GetName(), "throws");
+
   MemberOffset field_offset = field->GetOffset();
-  return GetFieldObject<ObjectArray<ObjectArray<Class>>>(field_offset);
+  auto* throws_array = GetFieldObject<ObjectArray<ObjectArray<Class>>>(field_offset);
+  CHECK(throws_array != nullptr);
+  return throws_array;
 }
 
 inline MemberOffset Class::GetDisableIntrinsicFlagOffset() {
@@ -796,8 +810,8 @@
     return 0;
   } else if (IsArrayClass()) {
     return 2;
-  } else if (IsProxyClass()) {
-    mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
+  } else if (IsAnyProxyClass()) {
+    mirror::ObjectArray<mirror::Class>* interfaces = GetInterfacesForAnyProxy();
     return interfaces != nullptr ? interfaces->GetLength() : 0;
   } else {
     const DexFile::TypeList* interfaces = GetInterfaceTypeList();
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 05a9039..b201293 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -538,6 +538,7 @@
 
 ArtMethod* Class::FindClassInitializer(size_t pointer_size) {
   for (ArtMethod& method : GetDirectMethods(pointer_size)) {
+    DCHECK(reinterpret_cast<volatile void*>(&method) != nullptr);
     if (method.IsClassInitializer()) {
       DCHECK_STREQ(method.GetName(), "<clinit>");
       DCHECK_STREQ(method.GetSignature().ToString().c_str(), "()V");
@@ -742,8 +743,8 @@
     return Primitive::Descriptor(GetPrimitiveType());
   } else if (IsArrayClass()) {
     return GetArrayDescriptor(storage);
-  } else if (IsProxyClass()) {
-    *storage = Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(this);
+  } else if (IsAnyProxyClass()) {
+    *storage = Runtime::Current()->GetClassLinker()->GetDescriptorForAnyProxy(this);
     return storage->c_str();
   } else {
     const DexFile& dex_file = GetDexFile();
@@ -786,8 +787,10 @@
       DCHECK_EQ(1U, idx);
       return class_linker->FindSystemClass(self, "Ljava/io/Serializable;");
     }
-  } else if (klass->IsProxyClass()) {
-    mirror::ObjectArray<mirror::Class>* interfaces = klass.Get()->GetInterfaces();
+  } else if (klass->IsAnyProxyClass()) {
+    // Proxies don't have a dex cache, so look at the
+    // interfaces through the magic static field "interfaces" from the proxy class itself.
+    mirror::ObjectArray<mirror::Class>* interfaces = klass.Get()->GetInterfacesForAnyProxy();
     DCHECK(interfaces != nullptr);
     return interfaces->Get(idx);
   } else {
@@ -826,7 +829,7 @@
 
 std::string Class::GetLocation() {
   mirror::DexCache* dex_cache = GetDexCache();
-  if (dex_cache != nullptr && !IsProxyClass()) {
+  if (dex_cache != nullptr && !IsAnyProxyClass()) {
     return dex_cache->GetLocation()->ToModifiedUtf8();
   }
   // Arrays and proxies are generated and have no corresponding dex file location.
@@ -944,9 +947,9 @@
   return new_class->AsClass();
 }
 
-bool Class::ProxyDescriptorEquals(const char* match) {
-  DCHECK(IsProxyClass());
-  return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(this) == match;
+bool Class::AnyProxyDescriptorEquals(const char* match) {
+  DCHECK(IsAnyProxyClass());
+  return Runtime::Current()->GetClassLinker()->GetDescriptorForAnyProxy(this) == match;
 }
 
 // TODO: Move this to java_lang_Class.cc?
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 0ab5b97..fcfb4b9 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -352,8 +352,16 @@
   static String* ComputeName(Handle<Class> h_this) SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!Roles::uninterruptible_);
 
+  // Is this either a java.lang.reflect.Proxy or a boxed lambda (java.lang.LambdaProxy)?
+  // -- Most code doesn't need to make the distinction, and this is the preferred thing to check.
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  bool IsProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+  bool IsAnyProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return IsReflectProxyClass() || IsLambdaProxyClass();
+  }
+
+  // Is this a java.lang.reflect.Proxy ?
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsReflectProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
     // Read access flags without using getter as whether something is a proxy can be check in
     // any loaded state
     // TODO: switch to a check if the super class is java.lang.reflect.Proxy?
@@ -361,6 +369,17 @@
     return (access_flags & kAccClassIsProxy) != 0;
   }
 
+  // Is this a boxed lambda (java.lang.LambdaProxy)?
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool IsLambdaProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+    // Read access flags without using getter as whether something is a proxy can be check in
+    // any loaded state
+    // TODO: switch to a check if the super class is java.lang.reflect.Proxy?
+    uint32_t access_flags = GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+    return (access_flags & kAccClassIsLambdaProxy) != 0;
+  }
+
+
   static MemberOffset PrimitiveTypeOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_);
   }
@@ -677,6 +696,8 @@
     return MemberOffset(OFFSETOF_MEMBER(Class, super_class_));
   }
 
+  // Returns the class's ClassLoader.
+  // A null value is returned if and only if this is a boot classpath class.
   ClassLoader* GetClassLoader() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_);
 
   void SetClassLoader(ClassLoader* new_cl) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1076,6 +1097,8 @@
 
   bool DescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Returns the backing DexFile's class definition for this class.
+  // This returns null if and only if the class has no backing DexFile.
   const DexFile::ClassDef* GetClassDef() SHARED_REQUIRES(Locks::mutator_lock_);
 
   ALWAYS_INLINE uint32_t NumDirectInterfaces() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1102,11 +1125,15 @@
                 size_t pointer_size)
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
-  // For proxy class only.
-  ObjectArray<Class>* GetInterfaces() SHARED_REQUIRES(Locks::mutator_lock_);
+  // For any proxy class only. Returns list of directly implemented interfaces.
+  // The value returned is always non-null.
+  ObjectArray<Class>* GetInterfacesForAnyProxy() SHARED_REQUIRES(Locks::mutator_lock_);
 
-  // For proxy class only.
-  ObjectArray<ObjectArray<Class>>* GetThrows() SHARED_REQUIRES(Locks::mutator_lock_);
+  // For any proxy class only. Returns a 2d array of classes.
+  // -- The 0th dimension correponds to the vtable index.
+  // -- The 1st dimension is a list of checked exception classes.
+  // The value returned is always non-null.
+  ObjectArray<ObjectArray<Class>>* GetThrowsForAnyProxy() SHARED_REQUIRES(Locks::mutator_lock_);
 
   // For reference class only.
   MemberOffset GetDisableIntrinsicFlagOffset() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1194,7 +1221,7 @@
   IterationRange<StrideIterator<ArtField>> GetIFieldsUnchecked()
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool ProxyDescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_);
+  bool AnyProxyDescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Check that the pointer size matches the one in the class linker.
   ALWAYS_INLINE static void CheckPointerSize(size_t pointer_size);
diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h
index e22ddd7..3139117 100644
--- a/runtime/mirror/class_loader-inl.h
+++ b/runtime/mirror/class_loader-inl.h
@@ -21,6 +21,7 @@
 
 #include "base/mutex-inl.h"
 #include "class_table-inl.h"
+#include "lambda/box_class_table-inl.h"
 
 namespace art {
 namespace mirror {
@@ -35,6 +36,10 @@
   if (class_table != nullptr) {
     class_table->VisitRoots(visitor);
   }
+  lambda::BoxClassTable* const lambda_box_class_table = GetLambdaProxyCache();
+  if (lambda_box_class_table != nullptr) {
+    lambda_box_class_table->VisitRoots(visitor);
+  }
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index c2a65d6..9d4fe96 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -24,6 +24,12 @@
 struct ClassLoaderOffsets;
 class ClassTable;
 
+namespace lambda {
+
+class BoxClassTable;
+
+}  // namespace lambda
+
 namespace mirror {
 
 class Class;
@@ -60,6 +66,16 @@
                       reinterpret_cast<uint64_t>(allocator));
   }
 
+  lambda::BoxClassTable* GetLambdaProxyCache() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return reinterpret_cast<lambda::BoxClassTable*>(
+        GetField64(OFFSET_OF_OBJECT_MEMBER(ClassLoader, lambda_proxy_cache_)));
+  }
+
+  void SetLambdaProxyCache(lambda::BoxClassTable* cache) SHARED_REQUIRES(Locks::mutator_lock_) {
+    SetField64<false>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, lambda_proxy_cache_),
+                      reinterpret_cast<uint64_t>(cache));
+  }
+
  private:
   // Visit instance fields of the class loader as well as its associated classes.
   // Null class loader is handled by ClassLinker::VisitClassRoots.
@@ -76,6 +92,7 @@
   uint32_t padding_ ATTRIBUTE_UNUSED;
   uint64_t allocator_;
   uint64_t class_table_;
+  uint64_t lambda_proxy_cache_;
 
   friend struct art::ClassLoaderOffsets;  // for verifying offset information
   friend class Object;  // For VisitReferences
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index 8a0daec..49c443e 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -57,14 +57,15 @@
   const auto pointer_size = kTransactionActive ?
       Runtime::Current()->GetClassLinker()->GetImagePointerSize() : sizeof(void*);
   auto dex_field_index = field->GetDexFieldIndex();
-  auto* resolved_field = field->GetDexCache()->GetResolvedField(dex_field_index, pointer_size);
-  if (field->GetDeclaringClass()->IsProxyClass()) {
+  if (field->GetDeclaringClass()->IsAnyProxyClass()) {
     DCHECK(field->IsStatic());
     DCHECK_LT(dex_field_index, 2U);
     // The two static fields (interfaces, throws) of all proxy classes
     // share the same dex file indices 0 and 1. So, we can't resolve
     // them in the dex cache.
   } else {
+    ArtField* resolved_field =
+        field->GetDexCache()->GetResolvedField(dex_field_index, pointer_size);
     if (resolved_field != nullptr) {
       DCHECK_EQ(resolved_field, field);
     } else {
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index ff6847c..b02e5b5 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -56,7 +56,7 @@
 
 ArtField* Field::GetArtField() {
   mirror::Class* declaring_class = GetDeclaringClass();
-  if (UNLIKELY(declaring_class->IsProxyClass())) {
+  if (UNLIKELY(declaring_class->IsAnyProxyClass())) {
     DCHECK(IsStatic());
     DCHECK_EQ(declaring_class->NumStaticFields(), 2U);
     // 0 == Class[] interfaces; 1 == Class[][] throws;
diff --git a/runtime/mirror/lambda_proxy.h b/runtime/mirror/lambda_proxy.h
new file mode 100644
index 0000000..cff3a12
--- /dev/null
+++ b/runtime/mirror/lambda_proxy.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_LAMBDA_PROXY_H_
+#define ART_RUNTIME_MIRROR_LAMBDA_PROXY_H_
+
+#include "lambda/closure.h"
+#include "object.h"
+
+namespace art {
+
+struct LambdaProxyOffsets;
+
+namespace mirror {
+
+// C++ mirror of a lambda proxy. Does not yet have a Java-equivalent source file.
+class MANAGED LambdaProxy FINAL : public Object {
+ public:
+  // Note that the runtime subclasses generate the following static fields:
+
+  // private static java.lang.Class[] interfaces;  // Declared interfaces for the lambda interface.
+  static constexpr size_t kStaticFieldIndexInterfaces = 0;
+  // private static java.lang.Class[][] throws;    // Maps vtable id to list of classes.
+  static constexpr size_t kStaticFieldIndexThrows = 1;
+  static constexpr size_t kStaticFieldCount = 2;   // Number of fields total.
+
+  // The offset from the start of 'LambdaProxy' object, to the closure_ field, in bytes.
+  // -- This is exposed publically in order to avoid exposing 'closure_' publically.
+  // -- Only meant to be used in stubs and other compiled code, not in runtime.
+  static inline MemberOffset GetInstanceFieldOffsetClosure() {
+    return OFFSET_OF_OBJECT_MEMBER(LambdaProxy, closure_);
+  }
+
+  // Direct methods available on the class:
+  static constexpr size_t kDirectMethodIndexConstructor = 0;  // <init>()V
+  static constexpr size_t kDirectMethodCount = 1;             // Only the constructor.
+
+  // Accessors to the fields:
+
+  // Get the native closure pointer. Usually non-null outside of lambda proxy contexts.
+  lambda::Closure* GetClosure() SHARED_REQUIRES(Locks::mutator_lock_) {
+    return reinterpret_cast<lambda::Closure*>(
+        GetField64(GetInstanceFieldOffsetClosure()));
+  }
+
+  // Set the native closure pointer. Usually should be non-null outside of lambda proxy contexts.
+  void SetClosure(lambda::Closure* closure) SHARED_REQUIRES(Locks::mutator_lock_) {
+    SetField64<false>(GetInstanceFieldOffsetClosure(),
+                      reinterpret_cast<uint64_t>(closure));
+  }
+
+ private:
+  // Instance fields, present in the base class and every generated subclass:
+
+  // private long closure;
+  union {
+    lambda::Closure* actual;
+    uint64_t padding;         // Don't trip up GetObjectSize checks, since the Java code has a long.
+  } closure_;
+
+  // Friends for generating offset tests:
+  friend struct art::LambdaProxyOffsets;              // for verifying offset information
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(LambdaProxy);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_LAMBDA_PROXY_H_
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 9946eab..36aa57f 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -54,6 +54,8 @@
 // if any particular method needs to be a default conflict. Used to figure out at runtime if
 // invoking this method will throw an exception.
 static constexpr uint32_t kAccDefaultConflict =      0x00800000;  // method (runtime)
+// Set by the class linker when creating a class that's a subtype of LambdaProxy.
+static constexpr uint32_t kAccClassIsLambdaProxy =   0x01000000;  // class  (dex only)
 
 // Special runtime-only flags.
 // Interface and all its super-interfaces with default methods have been recursively initialized.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4cd3c3d..da6cf1f 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -155,7 +155,9 @@
                                          jstring javaOutputName,
                                          jint flags ATTRIBUTE_UNUSED,
                                          // class_loader will be used for app images.
-                                         jobject class_loader ATTRIBUTE_UNUSED) {
+                                         jobject class_loader ATTRIBUTE_UNUSED,
+                                         // dex_elements will be used for app images.
+                                         jobject dex_elements ATTRIBUTE_UNUSED) {
   ScopedUtfChars sourceName(env, javaSourceName);
   if (sourceName.c_str() == nullptr) {
     return 0;
@@ -445,7 +447,12 @@
   NATIVE_METHOD(DexFile, getDexOptNeeded,
                 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
   NATIVE_METHOD(DexFile, openDexFileNative,
-                "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;)Ljava/lang/Object;"),
+                "(Ljava/lang/String;"
+                "Ljava/lang/String;"
+                "I"
+                "Ljava/lang/ClassLoader;"
+                "[Ldalvik/system/DexPathList$Element;"
+                ")Ljava/lang/Object;"),
 };
 
 void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 5e42392..6cebd4d 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -103,7 +103,7 @@
 static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Class* c = DecodeClass(soa, javaThis);
-  return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
+  return soa.AddLocalReference<jobjectArray>(c->GetInterfacesForAnyProxy()->Clone(soa.Self()));
 }
 
 static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
@@ -489,7 +489,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
   Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
@@ -501,7 +501,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     // Return an empty array instead of a null pointer.
     mirror::Class* annotation_array_class =
         soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
@@ -517,7 +517,7 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
   mirror::ObjectArray<mirror::Class>* classes = nullptr;
-  if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) {
+  if (!klass->IsAnyProxyClass() && klass->GetDexCache() != nullptr) {
     classes = klass->GetDexFile().GetDeclaredClasses(klass);
   }
   if (classes == nullptr) {
@@ -543,7 +543,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
   return soa.AddLocalReference<jclass>(klass->GetDexFile().GetEnclosingClass(klass));
@@ -553,7 +553,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
   mirror::Object* method = klass->GetDexFile().GetEnclosingMethod(klass);
@@ -570,7 +570,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
   mirror::Object* method = klass->GetDexFile().GetEnclosingMethod(klass);
@@ -587,7 +587,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return defaultValue;
   }
   uint32_t flags;
@@ -601,7 +601,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
   mirror::String* class_name = nullptr;
@@ -615,7 +615,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return false;
   }
   mirror::String* class_name = nullptr;
@@ -630,7 +630,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return false;
   }
   Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
@@ -641,7 +641,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
-  if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+  if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
   // Return null for anonymous classes.
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index aac800a..9166ecc 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -419,7 +419,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
-  if (field->GetDeclaringClass()->IsProxyClass()) {
+  if (field->GetDeclaringClass()->IsAnyProxyClass()) {
     return nullptr;
   }
   Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
@@ -429,7 +429,7 @@
 static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) {
   ScopedFastNativeObjectAccess soa(env);
   ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
-  if (field->GetDeclaringClass()->IsProxyClass()) {
+  if (field->GetDeclaringClass()->IsAnyProxyClass()) {
     // Return an empty array instead of a null pointer.
     mirror::Class* annotation_array_class =
         soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
@@ -443,7 +443,7 @@
 static jobjectArray Field_getSignatureAnnotation(JNIEnv* env, jobject javaField) {
   ScopedFastNativeObjectAccess soa(env);
   ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
-  if (field->GetDeclaringClass()->IsProxyClass()) {
+  if (field->GetDeclaringClass()->IsAnyProxyClass()) {
     return nullptr;
   }
   return soa.AddLocalReference<jobjectArray>(
@@ -455,7 +455,7 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
-  if (field->GetDeclaringClass()->IsProxyClass()) {
+  if (field->GetDeclaringClass()->IsAnyProxyClass()) {
     return false;
   }
   Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index caacba6..7894c9b 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -32,7 +32,7 @@
 static jobject Method_getAnnotationNative(JNIEnv* env, jobject javaMethod, jclass annotationType) {
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
+  if (method->GetDeclaringClass()->IsAnyProxyClass()) {
     return nullptr;
   }
   StackHandleScope<1> hs(soa.Self());
@@ -44,7 +44,7 @@
 static jobjectArray Method_getDeclaredAnnotations(JNIEnv* env, jobject javaMethod) {
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
+  if (method->GetDeclaringClass()->IsAnyProxyClass()) {
     // Return an empty array instead of a null pointer.
     mirror::Class* annotation_array_class =
         soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
@@ -67,7 +67,7 @@
 static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) {
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
+  if (method->GetDeclaringClass()->IsAnyProxyClass()) {
     mirror::Class* klass = method->GetDeclaringClass();
     int throws_index = -1;
     size_t i = 0;
@@ -79,7 +79,8 @@
       ++i;
     }
     CHECK_NE(throws_index, -1);
-    mirror::ObjectArray<mirror::Class>* declared_exceptions = klass->GetThrows()->Get(throws_index);
+    mirror::ObjectArray<mirror::Class>* declared_exceptions =
+        klass->GetThrowsForAnyProxy()->Get(throws_index);
     return soa.AddLocalReference<jobjectArray>(declared_exceptions->Clone(soa.Self()));
   } else {
     mirror::ObjectArray<mirror::Class>* result_array =
@@ -104,7 +105,7 @@
 static jobjectArray Method_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) {
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
+  if (method->GetDeclaringClass()->IsAnyProxyClass()) {
     return nullptr;
   }
   return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetParameterAnnotations(method));
@@ -120,7 +121,7 @@
                                                  jclass annotationType) {
   ScopedFastNativeObjectAccess soa(env);
   ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
+  if (method->GetDeclaringClass()->IsAnyProxyClass()) {
     return false;
   }
   StackHandleScope<1> hs(soa.Self());
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 4a6ab40..647cec0 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -27,15 +27,31 @@
 namespace art {
 
 static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces,
-                                  jobject loader, jobjectArray methods, jobjectArray throws) {
+                                  jobject loader, jobjectArray methods, jobjectArray throws,
+                                  jboolean is_lambda_proxy) {
   ScopedFastNativeObjectAccess soa(env);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  return soa.AddLocalReference<jclass>(class_linker->CreateProxyClass(
-      soa, name, interfaces, loader, methods, throws));
+
+  mirror::Class* proxy_class = nullptr;
+
+  if (UNLIKELY(is_lambda_proxy)) {
+    bool already_exists;  // XX: Perhaps add lambdaProxyCache to java.lang.ClassLoader ?
+    proxy_class = class_linker->CreateLambdaProxyClass(soa,
+                                                       name,
+                                                       interfaces,
+                                                       loader,
+                                                       methods,
+                                                       throws,
+                                                       /*out*/&already_exists);
+  } else {
+    proxy_class = class_linker->CreateProxyClass(soa, name, interfaces, loader, methods, throws);
+  }
+
+  return soa.AddLocalReference<jclass>(proxy_class);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;)Ljava/lang/Class;"),
+  NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;Z)Ljava/lang/Class;"),
 };
 
 void register_java_lang_reflect_Proxy(JNIEnv* env) {
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 57472ad..57aafcc 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -121,7 +121,7 @@
       GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
   interfaces.clear();  // Don't least possibly stale objects in the array as good practice.
   ASSERT_TRUE(proxy_class.Get() != nullptr);
-  ASSERT_TRUE(proxy_class->IsProxyClass());
+  ASSERT_TRUE(proxy_class->IsReflectProxyClass());
   ASSERT_TRUE(proxy_class->IsInitialized());
 
   EXPECT_EQ(2U, proxy_class->NumDirectInterfaces());  // Interfaces$I and Interfaces$J.
@@ -157,7 +157,7 @@
   }
 
   ASSERT_TRUE(proxyClass.Get() != nullptr);
-  ASSERT_TRUE(proxyClass->IsProxyClass());
+  ASSERT_TRUE(proxyClass->IsReflectProxyClass());
   ASSERT_TRUE(proxyClass->IsInitialized());
 
   EXPECT_TRUE(proxyClass->GetIFieldsPtr() == nullptr);
@@ -208,10 +208,10 @@
   }
 
   ASSERT_TRUE(proxyClass0.Get() != nullptr);
-  ASSERT_TRUE(proxyClass0->IsProxyClass());
+  ASSERT_TRUE(proxyClass0->IsReflectProxyClass());
   ASSERT_TRUE(proxyClass0->IsInitialized());
   ASSERT_TRUE(proxyClass1.Get() != nullptr);
-  ASSERT_TRUE(proxyClass1->IsProxyClass());
+  ASSERT_TRUE(proxyClass1->IsReflectProxyClass());
   ASSERT_TRUE(proxyClass1->IsInitialized());
 
   LengthPrefixedArray<ArtField>* static_fields0 = proxyClass0->GetSFieldsPtr();
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 9098d38..2ff9fd2 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -172,12 +172,23 @@
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
-  } else if (m->IsProxyMethod()) {
+  } else if (m->IsReflectProxyMethod()) {
     if (cur_quick_frame_ != nullptr) {
       return artQuickGetProxyThisObject(cur_quick_frame_);
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
+  } else if (m->IsLambdaProxyMethod()) {
+    if (cur_quick_frame_ != nullptr) {
+      // XX: Should be safe to return null here, the lambda proxies
+      // don't set up their own quick frame because they don't need to spill any registers.
+      // By the time we are executing inside of the final target of the proxy invoke,
+      // the original 'this' reference is no longer live.
+      LOG(WARNING) << "Lambda proxies don't have a quick frame, do they?!";
+      return nullptr;
+    } else {
+      return cur_shadow_frame_->GetVRegReference(0);
+    }
   } else {
     const DexFile::CodeItem* code_item = m->GetCodeItem();
     if (code_item == nullptr) {
@@ -814,7 +825,27 @@
     // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader.
     DCHECK(!method->IsDirect() && !method->IsConstructor())
         << "Constructors of proxy classes must have a OatQuickMethodHeader";
-    return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+
+    if (method->IsReflectProxyMethod()) {
+      return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+    } else if (method->IsLambdaProxyMethod()) {
+      // Set this to true later once every stub works without a frame.
+      // This is currently 'false' because using a closure as a "long"
+      // requires a quick frame to be set up on 32-bit architectures.
+      constexpr bool kLambdaProxyStubsSupportFrameless = false;
+      if (kIsDebugBuild || !kLambdaProxyStubsSupportFrameless) {
+        // When debugging we always use the 'RefAndArgs' quick frame to allow us
+        // to see a runtime stub when unwinding.
+        return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+      } else {
+        // Lambda proxies don't bother setting up a quick frame for release builds.
+        LOG(FATAL) << "Requested QuickMethodFrameInfo for a lambda proxy,"
+                   << "but it doesn't have one, for method: " << PrettyMethod(method);
+        UNREACHABLE();
+      }
+    } else {
+      LOG(FATAL) << "Unknown type of proxy method " << PrettyMethod(method);
+    }
   }
 
   // The only remaining case is if the method is native and uses the generic JNI stub.
diff --git a/runtime/utils.h b/runtime/utils.h
index 3690f86..8b7941a 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -18,9 +18,11 @@
 #define ART_RUNTIME_UTILS_H_
 
 #include <pthread.h>
+#include <stdlib.h>
 
 #include <limits>
 #include <memory>
+#include <random>
 #include <string>
 #include <type_traits>
 #include <vector>
@@ -350,6 +352,26 @@
                  double* parsed_value,
                  UsageFn Usage);
 
+#if defined(__BIONIC__)
+struct Arc4RandomGenerator {
+  typedef uint32_t result_type;
+  static constexpr uint32_t min() { return std::numeric_limits<uint32_t>::min(); }
+  static constexpr uint32_t max() { return std::numeric_limits<uint32_t>::max(); }
+  uint32_t operator() () { return arc4random(); }
+};
+using RNG = Arc4RandomGenerator;
+#else
+using RNG = std::random_device;
+#endif
+
+template <typename T>
+T GetRandomNumber(T min, T max) {
+  CHECK_LT(min, max);
+  std::uniform_int_distribution<T> dist(min, max);
+  RNG rng;
+  return dist(rng);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 13c4722..17e88ce 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -136,6 +136,9 @@
 
   // A new allocation shouldn't alias with pre-existing values.
   static int test3(TestClass obj) {
+    // Do an allocation here to avoid the HLoadClass and HClinitCheck
+    // at the second allocation.
+    new TestClass();
     obj.i = 1;
     obj.next.j = 2;
     TestClass obj2 = new TestClass();
diff --git a/test/550-new-instance-clinit/expected.txt b/test/550-new-instance-clinit/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/550-new-instance-clinit/expected.txt
diff --git a/test/550-new-instance-clinit/info.txt b/test/550-new-instance-clinit/info.txt
new file mode 100644
index 0000000..c5fa3c7
--- /dev/null
+++ b/test/550-new-instance-clinit/info.txt
@@ -0,0 +1,3 @@
+Regression test for optimizing which used to treat
+HNewInstance as not having side effects even though it
+could invoke a clinit method.
diff --git a/test/550-new-instance-clinit/src/Main.java b/test/550-new-instance-clinit/src/Main.java
new file mode 100644
index 0000000..45e259e
--- /dev/null
+++ b/test/550-new-instance-clinit/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    int foo = Main.a;
+    new Bar();
+    foo = Main.a;
+    if (foo != 43) {
+      throw new Error("Expected 43, got " + foo);
+    }
+  }
+  static int a = 42;
+}
+
+class Bar {
+  static {
+    Main.a++;
+  }
+}
diff --git a/test/955-lambda-smali/expected.txt b/test/955-lambda-smali/expected.txt
index 16381e4..8afe4bc 100644
--- a/test/955-lambda-smali/expected.txt
+++ b/test/955-lambda-smali/expected.txt
@@ -26,3 +26,5 @@
 (CaptureVariables) (0-args, 1 captured variable 'D'): value is -Infinity
 (CaptureVariables) (0-args, 8 captured variable 'ZBCSIJFD'): value is true,R,∂,1000,12345678,3287471278325742,Infinity,-Infinity
 (CaptureVariables) Caught NPE
+(BoxInvoke) Hello boxing world! (0-args, no closure) void
+(BoxInvoke) Hello boxing world!(1-args, no closure) returned: 12345678
diff --git a/test/955-lambda-smali/smali/BoxInvoke.smali b/test/955-lambda-smali/smali/BoxInvoke.smali
new file mode 100644
index 0000000..8b53333
--- /dev/null
+++ b/test/955-lambda-smali/smali/BoxInvoke.smali
@@ -0,0 +1,103 @@
+#  Copyright (C) 2015 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.
+#
+.class public LBoxInvoke;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 1
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
+
+.method public static run()V
+    .registers 0
+
+    invoke-static {}, LBoxInvoke;->testBoxInvoke()V
+    invoke-static {}, LBoxInvoke;->forceGC()V
+
+    return-void
+.end method
+
+# Test that invoke-virtual works on boxed innate lambdas.
+.method public static testBoxInvoke()V
+    .registers 100
+
+    # Try invoking 0-arg void return lambda
+    create-lambda v0, LBoxInvoke;->doHelloWorld0(J)V
+    const-string v2, "Ljava/lang/Runnable;"
+    box-lambda v2, v0 # Ljava/lang/Runnable;
+    invoke-interface {v2}, Ljava/lang/Runnable;->run()V
+
+    # Try invoking 1-arg int return lambda
+    create-lambda v3, LBoxInvoke;->doHelloWorld1(JLjava/lang/Object;)I
+    const-string v5, "Ljava/lang/Comparable;"
+    box-lambda v5, v3 # Ljava/lang/Comparable;
+    const-string v6, "Hello boxing world!"
+    invoke-interface {v5, v6}, Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I
+    move-result v7
+    sget-object v8, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual {v8, v7}, Ljava/io/PrintStream;->println(I)V
+
+    return-void
+
+    # TODO: more tests once box-lambda can take a type descriptor.
+
+.end method
+
+#TODO: should use a closure type instead of a long.
+.method public static doHelloWorld0(J)V
+    .registers 4 # 1 wide parameters, 2 locals
+
+    const-string v0, "(BoxInvoke) Hello boxing world! (0-args, no closure) void"
+
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    return-void
+.end method
+
+#TODO: should use a closure type instead of a long.
+.method public static doHelloWorld1(JLjava/lang/Object;)I
+    # J = closure, L = obj, I = return type
+    .registers 6 # 1 wide parameters, 1 narrow parameter, 3 locals
+
+    # Prints "<before> $parameter1(Object) <after>:" without the line terminator. 
+
+    const-string v0, "(BoxInvoke) "
+
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    # System.out.print("<before>");
+    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V
+
+    # System.out.print(obj);
+    invoke-virtual {v1, p2}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    # System.out.print("<after>: ");
+    const-string v0, "(1-args, no closure) returned: "
+    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V
+
+    const v2, 12345678
+    return v2
+.end method
+
+# Force a GC. Used to ensure our weak reference table of boxed lambdas is getting swept.
+.method private static forceGC()V
+    .registers 1
+    invoke-static {}, Ljava/lang/Runtime;->getRuntime()Ljava/lang/Runtime;
+    move-result-object v0
+    invoke-virtual {v0}, Ljava/lang/Runtime;->gc()V
+
+    return-void
+.end method
diff --git a/test/955-lambda-smali/smali/BoxUnbox.smali b/test/955-lambda-smali/smali/BoxUnbox.smali
index 915de2d..157adb3 100644
--- a/test/955-lambda-smali/smali/BoxUnbox.smali
+++ b/test/955-lambda-smali/smali/BoxUnbox.smali
@@ -51,6 +51,7 @@
     .registers 3
 
     create-lambda v0, LBoxUnbox;->doHelloWorld(J)V
+    const-string v2, "Ljava/lang/Runnable;"
     box-lambda v2, v0 # v2 = box(v0)
     unbox-lambda v0, v2, J # v0 = unbox(v2)
     invoke-lambda v0, {}
@@ -63,7 +64,9 @@
    .registers 6 # 0 parameters, 6 locals
 
     create-lambda v0, LBoxUnbox;->doHelloWorld(J)V
+    const-string v2, "Ljava/lang/Runnable;"
     box-lambda v2, v0 # v2 = box(v0)
+    const-string v3, "Ljava/lang/Runnable;"
     box-lambda v3, v0 # v3 = box(v0)
 
     # The objects should be not-null, and they should have the same reference
@@ -116,6 +119,7 @@
     const v0, 0  # v0 = null
     const v1, 0  # v1 = null
 :start
+    const-string v2, "Ljava/lang/Runnable;"
     box-lambda v2, v0  # attempting to box a null lambda will throw NPE
 :end
     return-void
diff --git a/test/955-lambda-smali/smali/CaptureVariables.smali b/test/955-lambda-smali/smali/CaptureVariables.smali
index f18b7ff..531c259 100644
--- a/test/955-lambda-smali/smali/CaptureVariables.smali
+++ b/test/955-lambda-smali/smali/CaptureVariables.smali
@@ -243,6 +243,8 @@
     # TODO: create-lambda should not write to both v0 and v1
     invoke-lambda v0, {}
 
+    return-void
+
 .end method
 
 #TODO: should use a closure type instead of a long
diff --git a/test/955-lambda-smali/smali/Main.smali b/test/955-lambda-smali/smali/Main.smali
index 9892d61..e8ab84c 100644
--- a/test/955-lambda-smali/smali/Main.smali
+++ b/test/955-lambda-smali/smali/Main.smali
@@ -25,6 +25,7 @@
     invoke-static {}, LBoxUnbox;->run()V
     invoke-static {}, LMoveResult;->run()V
     invoke-static {}, LCaptureVariables;->run()V
+    invoke-static {}, LBoxInvoke;->run()V
 
 # TODO: add tests when verification fails
 
diff --git a/test/960-default-smali/build b/test/960-default-smali/build
index 4dc848c..b72afcd 100755
--- a/test/960-default-smali/build
+++ b/test/960-default-smali/build
@@ -22,7 +22,7 @@
 
 # Should we compile with Java source code. By default we will use Smali.
 USES_JAVA_SOURCE="false"
-if [[ $ARGS == *"--jvm"* ]]; then
+if [[ $@ == *"--jvm"* ]]; then
   USES_JAVA_SOURCE="true"
 elif [[ "$USE_JACK" == "true" ]]; then
   if $JACK -D jack.java.source.version=1.8 >& /dev/null; then
diff --git a/test/961-default-iface-resolution-generated/build b/test/961-default-iface-resolution-generated/build
index b4ced3e..005f76c 100755
--- a/test/961-default-iface-resolution-generated/build
+++ b/test/961-default-iface-resolution-generated/build
@@ -33,7 +33,7 @@
 
 # Should we compile with Java source code. By default we will use Smali.
 USES_JAVA_SOURCE="false"
-if [[ $ARGS == *"--jvm"* ]]; then
+if [[ $@ == *"--jvm"* ]]; then
   USES_JAVA_SOURCE="true"
 elif [[ $USE_JACK == "true" ]]; then
   if "$JACK" -D jack.java.source.version=1.8 >& /dev/null; then
diff --git a/test/LambdaInterfaces/LambdaInterfaces.java b/test/LambdaInterfaces/LambdaInterfaces.java
new file mode 100644
index 0000000..261163d
--- /dev/null
+++ b/test/LambdaInterfaces/LambdaInterfaces.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+class LambdaInterfaces {
+    interface I {
+        public int i();
+    }
+    interface J {
+        public String foo = "foo";
+        public void j1();
+    }
+    interface K extends J {
+    }
+    interface L {
+      public int sum(int a, int b);
+    }
+    interface C {
+      public String concat(String a, String b);
+    }
+}
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index de27a6f..0747712 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -43,9 +43,11 @@
 vm_args=""
 # By default, we run the whole JDWP test suite.
 test="org.apache.harmony.jpda.tests.share.AllTests"
+host="no"
 
 while true; do
   if [[ "$1" == "--mode=host" ]]; then
+    host="yes"
     # Specify bash explicitly since the art script cannot, since it has to run on the device
     # with mksh.
     art="bash ${OUT_DIR-out}/host/linux-x86/bin/art"
@@ -118,3 +120,15 @@
       --classpath $test_jar \
       --vm-arg -Xcompiler-option --vm-arg --debuggable \
       $test
+
+vogar_exit_status=$?
+
+echo "Killing stalled dalvikvm processes..."
+if [[ $host == "yes" ]]; then
+  pkill -9 -f /bin/dalvikvm
+else
+  adb shell pkill -9 -f /bin/dalvikvm
+fi
+echo "Done."
+
+exit $vogar_exit_status