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(¤t_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