ART: invoke-custom support
Adds invoke-custom instruction to the interpreter.
Bug: 33191717,30550796
Test: art/test/run-test --host 952
Change-Id: I3b754128649a8b3a00ade79ba2518d0e377f3a1e
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 9585ba2..c871301 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -123,6 +123,7 @@
"memory_region.cc",
"method_handles.cc",
"mirror/array.cc",
+ "mirror/call_site.cc",
"mirror/class.cc",
"mirror/class_ext.cc",
"mirror/dex_cache.cc",
@@ -131,6 +132,7 @@
"mirror/field.cc",
"mirror/method.cc",
"mirror/method_handle_impl.cc",
+ "mirror/method_handles_lookup.cc",
"mirror/method_type.cc",
"mirror/object.cc",
"mirror/reference.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7db8368..110241b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -70,6 +70,7 @@
#include "jni_internal.h"
#include "leb128.h"
#include "linear_alloc.h"
+#include "mirror/call_site.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
@@ -82,6 +83,7 @@
#include "mirror/method.h"
#include "mirror/method_type.h"
#include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
@@ -695,6 +697,18 @@
SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
mirror::MethodHandleImpl::SetClass(class_root);
+ // Create java.lang.invoke.MethodHandles.Lookup.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root);
+ mirror::MethodHandlesLookup::SetClass(class_root);
+
+ // Create java.lang.invoke.CallSite.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeCallSite, class_root);
+ mirror::CallSite::SetClass(class_root);
+
class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
CHECK(class_root != nullptr);
SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root);
@@ -981,6 +995,8 @@
mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
+ mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup));
+ mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -1231,12 +1247,13 @@
if (dex_file->NumProtoIds() < num_method_types) {
num_method_types = dex_file->NumProtoIds();
}
-
+ const size_t num_call_sites = dex_file->NumCallSiteIds();
CHECK_EQ(num_strings, dex_cache->NumStrings());
CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
+ CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
DexCacheArraysLayout layout(image_pointer_size_, dex_file);
uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
if (num_strings != 0u) {
@@ -1316,6 +1333,22 @@
mirror::MethodTypeDexCachePair::Initialize(method_types);
dex_cache->SetResolvedMethodTypes(method_types);
}
+ if (num_call_sites != 0u) {
+ GcRoot<mirror::CallSite>* const image_resolved_call_sites =
+ dex_cache->GetResolvedCallSites();
+ GcRoot<mirror::CallSite>* const call_sites =
+ reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+ for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
+ DCHECK(call_sites[j].IsNull());
+ }
+ CopyNonNull(image_resolved_call_sites,
+ num_call_sites,
+ call_sites,
+ [](const GcRoot<mirror::CallSite>& elem) {
+ return elem.IsNull();
+ });
+ dex_cache->SetResolvedCallSites(call_sites);
+ }
}
{
WriterMutexLock mu2(self, *Locks::dex_lock_);
@@ -2115,6 +2148,8 @@
mirror::ShortArray::ResetArrayClass();
mirror::MethodType::ResetClass();
mirror::MethodHandleImpl::ResetClass();
+ mirror::MethodHandlesLookup::ResetClass();
+ mirror::CallSite::ResetClass();
mirror::EmulatedStackFrame::ResetClass();
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
@@ -3112,6 +3147,7 @@
last_field_idx = field_idx;
}
}
+
// Load instance fields.
LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
allocator,
@@ -3128,6 +3164,7 @@
last_field_idx = field_idx;
}
}
+
if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
UNLIKELY(num_ifields != it.NumInstanceFields())) {
LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
@@ -8189,6 +8226,148 @@
return type.Get();
}
+mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx,
+ ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* const self = Thread::Current();
+ const DexFile* const dex_file = referrer->GetDexFile();
+ const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx);
+
+ union {
+ ArtField* field;
+ ArtMethod* method;
+ uintptr_t field_or_method;
+ } target;
+ uint32_t num_params;
+ mirror::MethodHandle::Kind kind;
+ DexFile::MethodHandleType handle_type =
+ static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
+ switch (handle_type) {
+ case DexFile::MethodHandleType::kStaticPut: {
+ kind = mirror::MethodHandle::Kind::kStaticPut;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ num_params = 1;
+ break;
+ }
+ case DexFile::MethodHandleType::kStaticGet: {
+ kind = mirror::MethodHandle::Kind::kStaticGet;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ num_params = 0;
+ break;
+ }
+ case DexFile::MethodHandleType::kInstancePut: {
+ kind = mirror::MethodHandle::Kind::kInstancePut;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ num_params = 2;
+ break;
+ }
+ case DexFile::MethodHandleType::kInstanceGet: {
+ kind = mirror::MethodHandle::Kind::kInstanceGet;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ num_params = 1;
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic: {
+ kind = mirror::MethodHandle::Kind::kInvokeStatic;
+ target.method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kStatic);
+ uint32_t shorty_length;
+ target.method->GetShorty(&shorty_length);
+ num_params = shorty_length - 1; // Remove 1 for return value.
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInstance: {
+ kind = mirror::MethodHandle::Kind::kInvokeVirtual;
+ target.method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kVirtual);
+ uint32_t shorty_length;
+ target.method->GetShorty(&shorty_length);
+ num_params = shorty_length - 1; // Remove 1 for return value.
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+ num_params = 0;
+ }
+ }
+
+ StackHandleScope<5> hs(self);
+ ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+ ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
+ Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+ mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
+ if (method_params.Get() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ Handle<mirror::Class> return_type;
+ switch (handle_type) {
+ case DexFile::MethodHandleType::kStaticPut: {
+ method_params->Set(0, target.field->GetType<true>());
+ return_type = hs.NewHandle(FindPrimitiveClass('V'));
+ break;
+ }
+ case DexFile::MethodHandleType::kStaticGet: {
+ return_type = hs.NewHandle(target.field->GetType<true>());
+ break;
+ }
+ case DexFile::MethodHandleType::kInstancePut: {
+ method_params->Set(0, target.field->GetDeclaringClass());
+ method_params->Set(1, target.field->GetType<true>());
+ return_type = hs.NewHandle(FindPrimitiveClass('V'));
+ break;
+ }
+ case DexFile::MethodHandleType::kInstanceGet: {
+ method_params->Set(0, target.field->GetDeclaringClass());
+ return_type = hs.NewHandle(target.field->GetType<true>());
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic:
+ case DexFile::MethodHandleType::kInvokeInstance: {
+ // TODO(oth): This will not work for varargs methods as this
+ // requires instantiating a Transformer. This resolution step
+ // would be best done in managed code rather than in the run
+ // time (b/35235705)
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+ DexFileParameterIterator it(*dex_file, target.method->GetPrototype());
+ for (int32_t i = 0; it.HasNext(); i++, it.Next()) {
+ const dex::TypeIndex type_idx = it.GetTypeIdx();
+ mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
+ if (nullptr == klass) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ method_params->Set(i, klass);
+ }
+ return_type = hs.NewHandle(target.method->GetReturnType(true));
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ // TODO(oth): b/35235705
+ UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+ }
+ }
+
+ if (return_type.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ Handle<mirror::MethodType>
+ mt(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
+ if (mt.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt);
+}
+
bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
return (entry_point == GetQuickResolutionStub()) ||
(quick_resolution_trampoline_ == entry_point);
@@ -8304,7 +8483,9 @@
"[Ljava/lang/reflect/Constructor;",
"[Ljava/lang/reflect/Field;",
"[Ljava/lang/reflect/Method;",
+ "Ljava/lang/invoke/CallSite;",
"Ljava/lang/invoke/MethodHandleImpl;",
+ "Ljava/lang/invoke/MethodHandles$Lookup;",
"Ljava/lang/invoke/MethodType;",
"Ljava/lang/ClassLoader;",
"Ljava/lang/Throwable;",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 62d3c29..e27a53d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -55,6 +55,8 @@
class DexCacheMethodHandlesTest_Open_Test;
class DexCacheTest_Open_Test;
class IfTable;
+ class MethodHandle;
+ class MethodHandlesLookup;
class MethodType;
template<class T> class ObjectArray;
class StackTraceElement;
@@ -106,7 +108,9 @@
kJavaLangReflectConstructorArrayClass,
kJavaLangReflectFieldArrayClass,
kJavaLangReflectMethodArrayClass,
+ kJavaLangInvokeCallSite,
kJavaLangInvokeMethodHandleImpl,
+ kJavaLangInvokeMethodHandlesLookup,
kJavaLangInvokeMethodType,
kJavaLangClassLoader,
kJavaLangThrowable,
@@ -366,6 +370,12 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+ // Resolve a method handle with a given ID from the DexFile. The
+ // result is not cached in the DexCache as the instance will only be
+ // used once in most circumstances.
+ mirror::MethodHandle* ResolveMethodHandle(uint32_t method_handle_idx, ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true on success, false if there's an exception pending.
// can_run_clinit=false allows the compiler to attempt to init a class,
// given the restriction that no <clinit> execution is possible.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 03105cb..ee8aed7 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -32,6 +32,7 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/heap.h"
#include "mirror/accessible_object.h"
+#include "mirror/call_site.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
#include "mirror/dex_cache.h"
@@ -40,6 +41,7 @@
#include "mirror/field.h"
#include "mirror/method_type.h"
#include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
@@ -669,11 +671,13 @@
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
@@ -762,6 +766,14 @@
}
};
+struct MethodHandlesLookupOffsets : public CheckOffsets<mirror::MethodHandlesLookup> {
+ MethodHandlesLookupOffsets() : CheckOffsets<mirror::MethodHandlesLookup>(
+ false, "Ljava/lang/invoke/MethodHandles$Lookup;") {
+ addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, allowed_modes_), "allowedModes");
+ addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, lookup_class_), "lookupClass");
+ }
+};
+
struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
false, "Ldalvik/system/EmulatedStackFrame;") {
@@ -772,6 +784,13 @@
}
};
+struct CallSiteOffsets : public CheckOffsets<mirror::CallSite> {
+ CallSiteOffsets() : CheckOffsets<mirror::CallSite>(
+ false, "Ljava/lang/invoke/CallSite;") {
+ addOffset(OFFSETOF_MEMBER(mirror::CallSite, target_), "target");
+ }
+};
+
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
// ClassLinker::LinkFields.
@@ -794,7 +813,9 @@
EXPECT_TRUE(MethodTypeOffsets().Check());
EXPECT_TRUE(MethodHandleOffsets().Check());
EXPECT_TRUE(MethodHandleImplOffsets().Check());
+ EXPECT_TRUE(MethodHandlesLookupOffsets().Check());
EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
+ EXPECT_TRUE(CallSiteOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index a44f79e..4f4bed0 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -126,6 +126,22 @@
mirror::Class::PrettyDescriptor(array_class).c_str()).c_str());
}
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+ va_end(args);
+}
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowWrappedException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+ va_end(args);
+}
+
// ClassCastException
void ThrowClassCastException(ObjPtr<mirror::Class> dest_type, ObjPtr<mirror::Class> src_type) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 76ea2ae..55a8938 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -56,6 +56,14 @@
ObjPtr<mirror::Class> array_class)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// ClassCircularityError
void ThrowClassCircularityError(ObjPtr<mirror::Class> c)
@@ -236,7 +244,7 @@
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
-// WrontMethodTypeException
+// WrongMethodTypeException
void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
mirror::MethodType* callsite_type)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cf90bca..20bd52b 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -264,11 +264,11 @@
// MethodHandle Types
enum class MethodHandleType : uint16_t { // private
- kPutStatic = 0x0000, // a setter for a given static field.
- kGetStatic = 0x0001, // a getter for a given static field.
- kPutInstance = 0x0002, // a setter for a given instance field.
- kGetInstance = 0x0003, // a getter for a given instance field.
- kInvokeStatic = 0x0004, // an invoker for a given static method
+ kStaticPut = 0x0000, // a setter for a given static field.
+ kStaticGet = 0x0001, // a getter for a given static field.
+ kInstancePut = 0x0002, // a setter for a given instance field.
+ kInstanceGet = 0x0003, // a getter for a given instance field.
+ kInvokeStatic = 0x0004, // an invoker for a given static method.
kInvokeInstance = 0x0005, // invoke_instance : an invoker for a given instance method. This
// can be any non-static method on any class (or interface) except
// for “<init>”.
@@ -279,9 +279,9 @@
// raw method_handle_item
struct MethodHandleItem {
uint16_t method_handle_type_;
- uint16_t reserved1_; // Reserved for future use.
- uint16_t field_or_method_idx_;
- uint16_t reserved2_; // Reserved for future use.
+ uint16_t reserved1_; // Reserved for future use.
+ uint16_t field_or_method_idx_; // Field index for accessors, method index otherwise.
+ uint16_t reserved2_; // Reserved for future use.
private:
DISALLOW_COPY_AND_ASSIGN(MethodHandleItem);
};
@@ -722,6 +722,20 @@
return num_method_handles_;
}
+ const MethodHandleItem& GetMethodHandle(uint32_t idx) const {
+ CHECK_LT(idx, NumMethodHandles());
+ return method_handles_[idx];
+ }
+
+ uint32_t NumCallSiteIds() const {
+ return num_call_site_ids_;
+ }
+
+ const CallSiteIdItem& GetCallSiteId(uint32_t idx) const {
+ CHECK_LT(idx, NumCallSiteIds());
+ return call_site_ids_[idx];
+ }
+
// Returns a pointer to the raw memory mapped class_data_item
const uint8_t* GetClassData(const ClassDef& class_def) const {
if (class_def.class_data_off_ == 0) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index d870e52..0b3f16a 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2374,10 +2374,10 @@
uint32_t index = item->field_or_method_idx_;
switch (method_handle_type) {
- case DexFile::MethodHandleType::kPutStatic:
- case DexFile::MethodHandleType::kGetStatic:
- case DexFile::MethodHandleType::kPutInstance:
- case DexFile::MethodHandleType::kGetInstance: {
+ case DexFile::MethodHandleType::kStaticPut:
+ case DexFile::MethodHandleType::kStaticGet:
+ case DexFile::MethodHandleType::kInstancePut:
+ case DexFile::MethodHandleType::kInstanceGet: {
LOAD_FIELD(field, index, "method_handle_item field_idx", return false);
break;
}
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 37f3ac9..091085a 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -407,6 +407,20 @@
break;
}
FALLTHROUGH_INTENDED;
+ case INVOKE_CUSTOM:
+ if (file != nullptr) {
+ os << opcode << " {";
+ uint32_t call_site_idx = VRegB_35c();
+ for (size_t i = 0; i < VRegA_35c(); ++i) {
+ if (i != 0) {
+ os << ", ";
+ }
+ os << "v" << arg[i];
+ }
+ os << "}, // call_site@" << call_site_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
default:
os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
<< ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -415,6 +429,8 @@
break;
}
case k3rc: {
+ uint16_t first_reg = VRegC_3rc();
+ uint16_t last_reg = VRegC_3rc() + VRegA_3rc() - 1;
switch (Opcode()) {
case INVOKE_VIRTUAL_RANGE:
case INVOKE_SUPER_RANGE:
@@ -423,7 +439,7 @@
case INVOKE_INTERFACE_RANGE:
if (file != nullptr) {
uint32_t method_idx = VRegB_3rc();
- os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
<< file->PrettyMethod(method_idx) << " // method@" << method_idx;
break;
}
@@ -431,14 +447,22 @@
case INVOKE_VIRTUAL_RANGE_QUICK:
if (file != nullptr) {
uint32_t method_idx = VRegB_3rc();
- os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
<< "// vtable@" << method_idx;
break;
}
FALLTHROUGH_INTENDED;
+ case INVOKE_CUSTOM_RANGE:
+ if (file != nullptr) {
+ uint32_t call_site_idx = VRegB_3rc();
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "// call_site@" << call_site_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
default:
- os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
- (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "thing@" << VRegB_3rc();
break;
}
break;
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 578550c..d269110 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -126,14 +126,15 @@
enum IndexType {
kIndexUnknown = 0,
- kIndexNone, // has no index
- kIndexTypeRef, // type reference index
- kIndexStringRef, // string reference index
- kIndexMethodRef, // method reference index
- kIndexFieldRef, // field reference index
- kIndexFieldOffset, // field offset (for static linked fields)
- kIndexVtableOffset, // vtable offset (for static linked methods)
- kIndexMethodAndProtoRef // method and a proto reference index (for invoke-polymorphic)
+ kIndexNone, // has no index
+ kIndexTypeRef, // type reference index
+ kIndexStringRef, // string reference index
+ kIndexMethodRef, // method reference index
+ kIndexFieldRef, // field reference index
+ kIndexFieldOffset, // field offset (for static linked fields)
+ kIndexVtableOffset, // vtable offset (for static linked methods)
+ kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic)
+ kIndexCallSiteRef, // call site reference index
};
enum Flags {
@@ -165,31 +166,32 @@
};
enum VerifyFlag {
- kVerifyNone = 0x000000,
- kVerifyRegA = 0x000001,
- kVerifyRegAWide = 0x000002,
- kVerifyRegB = 0x000004,
- kVerifyRegBField = 0x000008,
- kVerifyRegBMethod = 0x000010,
- kVerifyRegBNewInstance = 0x000020,
- kVerifyRegBString = 0x000040,
- kVerifyRegBType = 0x000080,
- kVerifyRegBWide = 0x000100,
- kVerifyRegC = 0x000200,
- kVerifyRegCField = 0x000400,
- kVerifyRegCNewArray = 0x000800,
- kVerifyRegCType = 0x001000,
- kVerifyRegCWide = 0x002000,
- kVerifyArrayData = 0x004000,
- kVerifyBranchTarget = 0x008000,
- kVerifySwitchTargets = 0x010000,
- kVerifyVarArg = 0x020000,
- kVerifyVarArgNonZero = 0x040000,
- kVerifyVarArgRange = 0x080000,
- kVerifyVarArgRangeNonZero = 0x100000,
- kVerifyRuntimeOnly = 0x200000,
- kVerifyError = 0x400000,
- kVerifyRegHPrototype = 0x800000
+ kVerifyNone = 0x0000000,
+ kVerifyRegA = 0x0000001,
+ kVerifyRegAWide = 0x0000002,
+ kVerifyRegB = 0x0000004,
+ kVerifyRegBField = 0x0000008,
+ kVerifyRegBMethod = 0x0000010,
+ kVerifyRegBNewInstance = 0x0000020,
+ kVerifyRegBString = 0x0000040,
+ kVerifyRegBType = 0x0000080,
+ kVerifyRegBWide = 0x0000100,
+ kVerifyRegC = 0x0000200,
+ kVerifyRegCField = 0x0000400,
+ kVerifyRegCNewArray = 0x0000800,
+ kVerifyRegCType = 0x0001000,
+ kVerifyRegCWide = 0x0002000,
+ kVerifyArrayData = 0x0004000,
+ kVerifyBranchTarget = 0x0008000,
+ kVerifySwitchTargets = 0x0010000,
+ kVerifyVarArg = 0x0020000,
+ kVerifyVarArgNonZero = 0x0040000,
+ kVerifyVarArgRange = 0x0080000,
+ kVerifyVarArgRangeNonZero = 0x0100000,
+ kVerifyRuntimeOnly = 0x0200000,
+ kVerifyError = 0x0400000,
+ kVerifyRegHPrototype = 0x0800000,
+ kVerifyRegBCallSite = 0x1000000
};
static constexpr uint32_t kMaxVarArgRegs = 5;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index ca2ce1d..a5ce3c2 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -271,8 +271,8 @@
V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
- V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \
- V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \
+ V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
+ V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xFF, UNUSED_FF, "unused-ff", k10x, kIndexUnknown, 0, kVerifyError)
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 3ef47c4..c2bca53 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2435,8 +2435,8 @@
// Wrap raw_method_handle in a Handle for safety.
StackHandleScope<5> hs(self);
- Handle<mirror::MethodHandleImpl> method_handle(
- hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle))));
+ Handle<mirror::MethodHandle> method_handle(
+ hs.NewHandle(ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(raw_method_handle))));
raw_method_handle = nullptr;
self->EndAssertNoThreadSuspension(old_cause);
@@ -2497,15 +2497,14 @@
// consecutive order.
uint32_t unused_args[Instruction::kMaxVarArgRegs] = {};
uint32_t first_callee_arg = first_arg + 1;
- const bool do_assignability_check = false;
- if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self,
- resolved_method,
- *shadow_frame,
- method_handle,
- method_type,
- unused_args,
- first_callee_arg,
- result)) {
+ if (!DoInvokePolymorphic<true /* is_range */>(self,
+ resolved_method,
+ *shadow_frame,
+ method_handle,
+ method_type,
+ unused_args,
+ first_callee_arg,
+ result)) {
DCHECK(self->IsExceptionPending());
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 442a42e..55bd1d4 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1283,6 +1283,14 @@
}
dex_cache->FixupResolvedMethodTypes<kWithoutReadBarrier>(new_method_types, fixup_adapter);
}
+ GcRoot<mirror::CallSite>* call_sites = dex_cache->GetResolvedCallSites();
+ if (call_sites != nullptr) {
+ GcRoot<mirror::CallSite>* new_call_sites = fixup_adapter.ForwardObject(call_sites);
+ if (call_sites != new_call_sites) {
+ dex_cache->SetResolvedCallSites(new_call_sites);
+ }
+ dex_cache->FixupResolvedCallSites<kWithoutReadBarrier>(new_call_sites, fixup_adapter);
+ }
}
}
{
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 28bcb97..3d4a2d1 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -519,7 +519,7 @@
}
}
-template<bool is_range, bool do_access_check>
+template<bool is_range>
bool DoInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
@@ -539,8 +539,8 @@
// was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact)
// and not the method that we'll dispatch to in the end.
StackHandleScope<5> hs(self);
- Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle(
- ObjPtr<mirror::MethodHandleImpl>::DownCast(
+ Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+ ObjPtr<mirror::MethodHandle>::DownCast(
MakeObjPtr(shadow_frame.GetVRegReference(vRegC)))));
if (UNLIKELY(method_handle.Get() == nullptr)) {
// Note that the invoke type is kVirtual here because a call to a signature
@@ -584,31 +584,300 @@
// VRegC is the register holding the method handle. Arguments passed
// to the method handle's target do not include the method handle.
uint32_t first_arg = inst->VRegC_4rcc() + 1;
- return DoInvokePolymorphic<is_range, do_access_check>(self,
- invoke_method,
- shadow_frame,
- method_handle,
- callsite_type,
- args /* unused */,
- first_arg,
- result);
+ return DoInvokePolymorphic<is_range>(self,
+ invoke_method,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args /* unused */,
+ first_arg,
+ result);
} else {
// Get the register arguments for the invoke.
inst->GetVarArgs(args, inst_data);
// Drop the first register which is the method handle performing the invoke.
memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
args[Instruction::kMaxVarArgRegs - 1] = 0;
- return DoInvokePolymorphic<is_range, do_access_check>(self,
- invoke_method,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- args[0],
- result);
+ return DoInvokePolymorphic<is_range>(self,
+ invoke_method,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ args[0],
+ result);
}
}
+static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t call_site_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* referrer = shadow_frame.GetMethod();
+ const DexFile* dex_file = referrer->GetDexFile();
+ const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
+
+ StackHandleScope<9> hs(self);
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+
+ CallSiteArrayValueIterator it(*dex_file, csi);
+ uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::MethodHandle>
+ bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(method_handle_idx, referrer)));
+ if (bootstrap.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ Handle<mirror::MethodType> bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType());
+ it.Next();
+
+ DCHECK_EQ(static_cast<size_t>(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size());
+ const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs();
+
+ // Set-up a shadow frame for invoking the bootstrap method handle.
+ ShadowFrameAllocaUniquePtr bootstrap_frame =
+ CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC());
+ ScopedStackedShadowFramePusher pusher(
+ self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
+ size_t vreg = 0;
+
+ // The first parameter is a MethodHandles lookup instance.
+ {
+ Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass()));
+ ObjPtr<mirror::MethodHandlesLookup> lookup =
+ mirror::MethodHandlesLookup::Create(self, lookup_class);
+ if (lookup.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr());
+ }
+
+ // The second parameter is the name to lookup.
+ {
+ dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i));
+ ObjPtr<mirror::String> name = class_linker->ResolveString(*dex_file, name_idx, dex_cache);
+ if (name.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg++, name.Ptr());
+ }
+ it.Next();
+
+ // The third parameter is the method type associated with the name.
+ uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ Handle<mirror::MethodType>
+ method_type(hs.NewHandle(class_linker->ResolveMethodType(*dex_file,
+ method_type_idx,
+ dex_cache,
+ class_loader)));
+ if (method_type.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg++, method_type.Get());
+ it.Next();
+
+ // Append remaining arguments (if any).
+ while (it.HasNext()) {
+ const jvalue& jvalue = it.GetJavaValue();
+ switch (it.GetValueType()) {
+ case EncodedArrayValueIterator::ValueType::kBoolean:
+ case EncodedArrayValueIterator::ValueType::kByte:
+ case EncodedArrayValueIterator::ValueType::kChar:
+ case EncodedArrayValueIterator::ValueType::kShort:
+ case EncodedArrayValueIterator::ValueType::kInt:
+ bootstrap_frame->SetVReg(vreg, jvalue.i);
+ vreg += 1;
+ break;
+ case EncodedArrayValueIterator::ValueType::kLong:
+ bootstrap_frame->SetVRegLong(vreg, jvalue.j);
+ vreg += 2;
+ break;
+ case EncodedArrayValueIterator::ValueType::kFloat:
+ bootstrap_frame->SetVRegFloat(vreg, jvalue.f);
+ vreg += 1;
+ break;
+ case EncodedArrayValueIterator::ValueType::kDouble:
+ bootstrap_frame->SetVRegDouble(vreg, jvalue.d);
+ vreg += 2;
+ break;
+ case EncodedArrayValueIterator::ValueType::kMethodType: {
+ uint32_t idx = static_cast<uint32_t>(jvalue.i);
+ ObjPtr<mirror::MethodType> ref =
+ class_linker->ResolveMethodType(*dex_file, idx, dex_cache, class_loader);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kMethodHandle: {
+ uint32_t idx = static_cast<uint32_t>(jvalue.i);
+ ObjPtr<mirror::MethodHandle> ref =
+ class_linker->ResolveMethodHandle(idx, referrer);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kString: {
+ dex::StringIndex idx(static_cast<uint32_t>(jvalue.i));
+ ObjPtr<mirror::String> ref = class_linker->ResolveString(*dex_file, idx, dex_cache);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kType: {
+ dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i));
+ ObjPtr<mirror::Class> ref =
+ class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kNull:
+ bootstrap_frame->SetVRegReference(vreg, nullptr);
+ vreg += 1;
+ break;
+ case EncodedArrayValueIterator::ValueType::kField:
+ case EncodedArrayValueIterator::ValueType::kMethod:
+ case EncodedArrayValueIterator::ValueType::kEnum:
+ case EncodedArrayValueIterator::ValueType::kArray:
+ case EncodedArrayValueIterator::ValueType::kAnnotation:
+ // Unreachable based on current EncodedArrayValueIterator::Next().
+ UNREACHABLE();
+ }
+
+ it.Next();
+ }
+
+ // Invoke the bootstrap method handle.
+ JValue result;
+
+ // This array of arguments is unused. DoInvokePolymorphic() operates on either a
+ // an argument array or a range, but always takes an array argument.
+ uint32_t args_unused[Instruction::kMaxVarArgRegs];
+ ArtMethod* invoke_exact =
+ jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ bool invoke_success = DoInvokePolymorphic<true /* is_range */>(self,
+ invoke_exact,
+ *bootstrap_frame,
+ bootstrap,
+ bootstrap_method_type,
+ args_unused,
+ 0,
+ &result);
+ if (!invoke_success) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ Handle<mirror::Object> object(hs.NewHandle(result.GetL()));
+
+ // Check the result is not null.
+ if (UNLIKELY(object.IsNull())) {
+ ThrowNullPointerException("CallSite == null");
+ return nullptr;
+ }
+
+ // Check the result type is a subclass of CallSite.
+ if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) {
+ ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass());
+ return nullptr;
+ }
+
+ Handle<mirror::CallSite> call_site =
+ hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
+
+ // Check the call site target is not null as we're going to invoke it.
+ Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+ if (UNLIKELY(target.IsNull())) {
+ ThrowNullPointerException("CallSite target == null");
+ return nullptr;
+ }
+
+ // Check the target method type matches the method type requested.
+ if (UNLIKELY(!target->GetMethodType()->IsExactMatch(method_type.Get()))) {
+ ThrowWrongMethodTypeException(target->GetMethodType(), method_type.Get());
+ return nullptr;
+ }
+
+ return call_site.Get();
+}
+
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // invoke-custom is not supported in transactions. In transactions
+ // there is a limited set of types supported. invoke-custom allows
+ // running arbitrary code and instantiating arbitrary types.
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ StackHandleScope<4> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
+ const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ MutableHandle<mirror::CallSite>
+ call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx)));
+ if (call_site.IsNull()) {
+ call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
+ if (UNLIKELY(call_site.IsNull())) {
+ CHECK(self->IsExceptionPending());
+ ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+ call_site_idx);
+ result->SetJ(0);
+ return false;
+ }
+ mirror::CallSite* winning_call_site =
+ dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get());
+ call_site.Assign(winning_call_site);
+ }
+
+ // CallSite.java checks the re-assignment of the call site target
+ // when mutating call site targets. We only check the target is
+ // non-null and has the right type during bootstrap method execution.
+ Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+ Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
+ DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
+
+ uint32_t args[Instruction::kMaxVarArgRegs];
+ if (is_range) {
+ args[0] = inst->VRegC_3rc();
+ } else {
+ inst->GetVarArgs(args, inst_data);
+ }
+
+ ArtMethod* invoke_exact =
+ jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ return DoInvokePolymorphic<is_range>(self,
+ invoke_exact,
+ shadow_frame,
+ target,
+ target_method_type,
+ args,
+ args[0],
+ result);
+}
+
template <bool is_range>
inline void CopyRegisters(ShadowFrame& caller_frame,
ShadowFrame* callee_frame,
@@ -975,17 +1244,24 @@
EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
-// Explicit DoInvokePolymorphic template function declarations.
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \
- Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
+// Explicit DoInvokeCustom template function declarations.
+#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokeCustom<_is_range>( \
+ Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokePolymorphic<_is_range>( \
+ Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
// Explicit DoFilledNewArray template function declarations.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 7ef3508..6b22af9 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -40,9 +40,11 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "handle_scope-inl.h"
#include "jit/jit.h"
+#include "mirror/call_site.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/method.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
@@ -154,13 +156,21 @@
}
// Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range).
-template<bool is_range, bool do_access_check>
+template<bool is_range>
bool DoInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
JValue* result);
+// Performs a custom invoke (invoke-custom/invoke-custom-range).
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result);
+
// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
// Returns true on success, otherwise throws an exception and returns false.
template<bool is_range>
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index a77a3fc..b191dd7 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -1524,7 +1524,7 @@
case Instruction::INVOKE_POLYMORPHIC: {
PREAMBLE();
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokePolymorphic<false, do_access_check>(
+ bool success = DoInvokePolymorphic<false /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
break;
@@ -1532,11 +1532,27 @@
case Instruction::INVOKE_POLYMORPHIC_RANGE: {
PREAMBLE();
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokePolymorphic<true, do_access_check>(
+ bool success = DoInvokePolymorphic<true /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
break;
}
+ case Instruction::INVOKE_CUSTOM: {
+ PREAMBLE();
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokeCustom<false /* is_range */>(
+ self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_CUSTOM_RANGE: {
+ PREAMBLE();
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokeCustom<true /* is_range */>(
+ self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
case Instruction::NEG_INT:
PREAMBLE();
shadow_frame.SetVReg(
@@ -2315,7 +2331,7 @@
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
- case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
UnexpectedOpcode(inst, shadow_frame);
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 99886e5..9825ad6 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -555,7 +555,7 @@
Handle<mirror::MethodType> callee_type,
Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> receiver,
+ Handle<mirror::MethodHandle> receiver,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
JValue* result)
@@ -645,7 +645,7 @@
template <bool is_range>
bool DoInvokePolymorphicUnchecked(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -780,7 +780,6 @@
}
// Helper for setters in invoke-polymorphic.
-template <bool do_assignability_check>
inline bool DoFieldPutForInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
ObjPtr<mirror::Object>& obj,
@@ -788,30 +787,33 @@
Primitive::Type field_type,
const JValue& value)
REQUIRES_SHARED(Locks::mutator_lock_) {
- static const bool kTransaction = false;
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ static const bool kTransaction = false; // Not in a transaction.
+ static const bool kAssignabilityCheck = false; // No access check.
switch (field_type) {
case Primitive::kPrimBoolean:
- return DoFieldPutCommon<Primitive::kPrimBoolean, do_assignability_check, kTransaction>(
- self, shadow_frame, obj, field, value);
+ return
+ DoFieldPutCommon<Primitive::kPrimBoolean, kAssignabilityCheck, kTransaction>(
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimByte:
- return DoFieldPutCommon<Primitive::kPrimByte, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimByte, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimChar:
- return DoFieldPutCommon<Primitive::kPrimChar, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimChar, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimShort:
- return DoFieldPutCommon<Primitive::kPrimShort, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimShort, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimInt:
case Primitive::kPrimFloat:
- return DoFieldPutCommon<Primitive::kPrimInt, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimInt, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimLong:
case Primitive::kPrimDouble:
- return DoFieldPutCommon<Primitive::kPrimLong, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimLong, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimNot:
- return DoFieldPutCommon<Primitive::kPrimNot, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimNot, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -855,10 +857,10 @@
return field_value;
}
-template <bool is_range, bool do_conversions, bool do_assignability_check>
+template <bool is_range, bool do_conversions>
bool DoInvokePolymorphicFieldAccess(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -903,12 +905,7 @@
return false;
}
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
- return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
- shadow_frame,
- obj,
- field,
- field_type,
- value);
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
}
case mirror::MethodHandle::kStaticPut: {
ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
@@ -922,12 +919,7 @@
DCHECK(self->IsExceptionPending());
return false;
}
- return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
- shadow_frame,
- obj,
- field,
- field_type,
- value);
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
}
default:
LOG(FATAL) << "Unreachable: " << handle_kind;
@@ -935,10 +927,10 @@
}
}
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
static inline bool DoInvokePolymorphicNonExact(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -959,7 +951,7 @@
if (IsFieldAccess(handle_kind)) {
if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) {
const bool do_convert = false;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+ return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -969,7 +961,7 @@
result);
} else {
const bool do_convert = true;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+ return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -999,10 +991,10 @@
}
}
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
bool DoInvokePolymorphicExact(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -1018,13 +1010,13 @@
ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
return false;
}
- return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return DoInvokePolymorphicNonExact<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
}
ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
@@ -1036,7 +1028,7 @@
const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
if (IsFieldAccess(handle_kind)) {
const bool do_convert = false;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+ return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -1057,51 +1049,49 @@
} // namespace
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
bool DoInvokePolymorphic(Thread* self,
ArtMethod* invoke_method,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (IsMethodHandleInvokeExact(invoke_method)) {
- return DoInvokePolymorphicExact<is_range, do_assignability_check>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return DoInvokePolymorphicExact<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
} else {
- return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return DoInvokePolymorphicNonExact<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
}
}
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
-template REQUIRES_SHARED(Locks::mutator_lock_) \
-bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \
- Thread* self, \
- ArtMethod* invoke_method, \
- ShadowFrame& shadow_frame, \
- Handle<mirror::MethodHandleImpl> method_handle, \
- Handle<mirror::MethodType> callsite_type, \
- const uint32_t (&args)[Instruction::kMaxVarArgRegs], \
- uint32_t first_arg, \
- JValue* result)
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokePolymorphic<_is_range>( \
+ Thread* self, \
+ ArtMethod* invoke_method, \
+ ShadowFrame& shadow_frame, \
+ Handle<mirror::MethodHandle> method_handle, \
+ Handle<mirror::MethodType> callsite_type, \
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs], \
+ uint32_t first_arg, \
+ JValue* result)
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
} // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 734d7c7..5bea0ab 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -27,7 +27,7 @@
namespace art {
namespace mirror {
- class MethodHandleImpl;
+ class MethodHandle;
class MethodType;
} // mirror
@@ -202,11 +202,11 @@
size_t arg_index_;
};
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
bool DoInvokePolymorphic(Thread* self,
ArtMethod* invoke_method,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc
new file mode 100644
index 0000000..eb613df
--- /dev/null
+++ b/runtime/mirror/call_site.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 "call_site.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> CallSite::static_class_;
+
+mirror::CallSite* CallSite::Create(Thread* const self, Handle<MethodHandle> target) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::CallSite> cs(
+ hs.NewHandle(ObjPtr<CallSite>::DownCast(StaticClass()->AllocObject(self))));
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ cs->SetFieldObject<false>(TargetOffset(), target.Get());
+ return cs.Get();
+}
+
+void CallSite::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void CallSite::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void CallSite::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h
new file mode 100644
index 0000000..db244a5
--- /dev/null
+++ b/runtime/mirror/call_site.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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_CALL_SITE_H_
+#define ART_RUNTIME_MIRROR_CALL_SITE_H_
+
+#include "mirror/method_handle_impl.h"
+#include "utils.h"
+
+namespace art {
+
+struct CallSiteOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.CallSite
+class MANAGED CallSite : public Object {
+ public:
+ static mirror::CallSite* Create(Thread* const self,
+ Handle<MethodHandle> method_handle)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<MethodHandle>(TargetOffset());
+ }
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static inline MemberOffset TargetOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(CallSite, target_));
+ }
+
+ HeapReference<mirror::MethodHandle> target_;
+
+ static GcRoot<mirror::Class> static_class_; // java.lang.invoke.CallSite.class
+
+ friend struct art::CallSiteOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_CALL_SITE_H_
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index a59bb7b..973c8ed 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -26,6 +26,7 @@
#include "base/logging.h"
#include "gc_root.h"
#include "mirror/class.h"
+#include "mirror/call_site.h"
#include "mirror/method_type.h"
#include "runtime.h"
#include "obj_ptr.h"
@@ -106,6 +107,35 @@
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
}
+inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+ GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+ Atomic<GcRoot<mirror::CallSite>>& ref =
+ reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+ return ref.LoadSequentiallyConsistent().Read();
+}
+
+inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+
+ GcRoot<mirror::CallSite> null_call_site(nullptr);
+ GcRoot<mirror::CallSite> candidate(call_site);
+ GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+
+ // The first assignment for a given call site wins.
+ Atomic<GcRoot<mirror::CallSite>>& ref =
+ reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+ if (ref.CompareExchangeStrongSequentiallyConsistent(null_call_site, candidate)) {
+ // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+ return call_site;
+ } else {
+ return target.Read();
+ }
+}
+
inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB.
@@ -208,6 +238,11 @@
VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>(
GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
+
+ GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites();
+ for (size_t i = 0, num_call_sites = NumResolvedCallSites(); i != num_call_sites; ++i) {
+ visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
+ }
}
}
@@ -246,6 +281,17 @@
}
}
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
+inline void DexCache::FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest,
+ const Visitor& visitor) {
+ GcRoot<mirror::CallSite>* src = GetResolvedCallSites();
+ for (size_t i = 0, count = NumResolvedCallSites(); i < count; ++i) {
+ mirror::CallSite* source = src[i].Read<kReadBarrierOption>();
+ mirror::CallSite* new_source = visitor(source);
+ dest[i] = GcRoot<mirror::CallSite>(new_source);
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 741cf3b..0f6acab 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -90,6 +90,10 @@
raw_arrays + layout.MethodTypesOffset());
}
+ GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0)
+ ? nullptr
+ : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+
DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
"Expected raw_arrays to align to StringDexCacheType.";
DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
@@ -117,6 +121,9 @@
CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
}
+ for (size_t i = 0; i < dex_file->NumCallSiteIds(); ++i) {
+ CHECK(call_sites[i].IsNull());
+ }
}
if (strings != nullptr) {
mirror::StringDexCachePair::Initialize(strings);
@@ -136,6 +143,8 @@
dex_file->NumFieldIds(),
method_types,
num_method_types,
+ call_sites,
+ dex_file->NumCallSiteIds(),
image_pointer_size);
}
@@ -151,6 +160,8 @@
uint32_t num_resolved_fields,
MethodTypeDexCacheType* resolved_method_types,
uint32_t num_resolved_method_types,
+ GcRoot<CallSite>* resolved_call_sites,
+ uint32_t num_resolved_call_sites,
PointerSize pointer_size) {
CHECK(dex_file != nullptr);
CHECK(location != nullptr);
@@ -159,6 +170,7 @@
CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
+ CHECK_EQ(num_resolved_call_sites != 0u, resolved_call_sites != nullptr);
SetDexFile(dex_file);
SetLocation(location);
@@ -167,11 +179,13 @@
SetResolvedMethods(resolved_methods);
SetResolvedFields(resolved_fields);
SetResolvedMethodTypes(resolved_method_types);
+ SetResolvedCallSites(resolved_call_sites);
SetField32<false>(NumStringsOffset(), num_strings);
SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
+ SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites);
Runtime* const runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 6f88cc5..10bb5aa 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -36,6 +36,7 @@
namespace mirror {
+class CallSite;
class MethodType;
class String;
@@ -163,6 +164,10 @@
void FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
+ void FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, const Visitor& visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
String* GetLocation() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_));
}
@@ -191,6 +196,10 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
}
+ static MemberOffset ResolvedCallSitesOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
+ }
+
static MemberOffset NumStringsOffset() {
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
}
@@ -211,6 +220,10 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
}
+ static MemberOffset NumResolvedCallSitesOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
+ }
+
mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -244,7 +257,18 @@
MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Attempts to bind |call_site_idx| to the call site |resolved|. The
+ // caller must use the return value in place of |resolved|. This is
+ // because multiple threads can invoke the bootstrap method each
+ // producing a call site, but the method handle invocation on the
+ // call site must be on a common agreed value.
+ CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED
+ REQUIRES_SHARED(Locks::mutator_lock_);
StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
@@ -295,6 +319,18 @@
SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
}
+ GcRoot<CallSite>* GetResolvedCallSites()
+ ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldPtr<GcRoot<CallSite>*>(ResolvedCallSitesOffset());
+ }
+
+ void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
+ ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
+ }
+
size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetField32(NumStringsOffset());
}
@@ -315,6 +351,10 @@
return GetField32(NumResolvedMethodTypesOffset());
}
+ size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetField32(NumResolvedCallSitesOffset());
+ }
+
const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
}
@@ -346,8 +386,10 @@
uint32_t num_resolved_methods,
ArtField** resolved_fields,
uint32_t num_resolved_fields,
- MethodTypeDexCacheType* resolved_methodtypes,
- uint32_t num_resolved_methodtypes,
+ MethodTypeDexCacheType* resolved_method_types,
+ uint32_t num_resolved_method_types,
+ GcRoot<CallSite>* resolved_call_sites,
+ uint32_t num_resolved_call_sites,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -362,6 +404,8 @@
HeapReference<Object> dex_;
HeapReference<String> location_;
uint64_t dex_file_; // const DexFile*
+ uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
+ // elements.
uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements.
uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
// num_resolved_method_types_ elements.
@@ -370,6 +414,7 @@
uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
// elements.
+ uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array.
uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array.
uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 4f1c448..fa4d25a 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -28,6 +28,18 @@
return klass;
}
+void MethodHandle::Initialize(uintptr_t art_field_or_method,
+ Kind kind,
+ Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr);
+ SetFieldObject<false>(NominalTypeOffset(), nullptr);
+ SetFieldObject<false>(MethodTypeOffset(), method_type.Get());
+ SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind));
+ SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method);
+}
+
GcRoot<mirror::Class> MethodHandleImpl::static_class_;
void MethodHandleImpl::SetClass(Class* klass) {
@@ -45,5 +57,17 @@
static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
+mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self,
+ uintptr_t art_field_or_method,
+ MethodHandle::Kind kind,
+ Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::MethodHandleImpl> mh(
+ hs.NewHandle(ObjPtr<MethodHandleImpl>::DownCast(StaticClass()->AllocObject(self))));
+ mh->Initialize(art_field_or_method, kind, method_type);
+ return mh.Get();
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 53d267b..9938af8 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -17,10 +17,11 @@
#ifndef ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
#define ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
+#include "art_field.h"
+#include "art_method.h"
#include "class.h"
#include "gc_root.h"
#include "object-inl.h"
-#include "method_handles.h"
#include "method_type.h"
namespace art {
@@ -82,10 +83,19 @@
GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
}
+ ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ Kind kind = GetHandleKind();
+ return (kind <= kLastValidKind) ?
+ GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass();
+ }
+
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ protected:
+ void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
- // NOTE: cached_spread_invoker_ isn't used by the runtime.
HeapReference<mirror::MethodHandle> cached_spread_invoker_;
HeapReference<mirror::MethodType> nominal_type_;
HeapReference<mirror::MethodType> method_type_;
@@ -93,6 +103,9 @@
uint64_t art_field_or_method_;
private:
+ static MemberOffset CachedSpreadInvokerOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_));
+ }
static MemberOffset NominalTypeOffset() {
return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_));
}
@@ -113,6 +126,12 @@
// C++ mirror of java.lang.invoke.MethodHandleImpl
class MANAGED MethodHandleImpl : public MethodHandle {
public:
+ static mirror::MethodHandleImpl* Create(Thread* const self,
+ uintptr_t art_field_or_method,
+ MethodHandle::Kind kind,
+ Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
return static_class_.Read();
}
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
new file mode 100644
index 0000000..c758e54
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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 "method_handles_lookup.h"
+
+#include "class.h"
+#include "gc_root-inl.h"
+#include "object-inl.h"
+#include "handle_scope.h"
+#include "modifiers.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> MethodHandlesLookup::static_class_;
+
+void MethodHandlesLookup::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void MethodHandlesLookup::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle<Class> lookup_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic;
+
+ StackHandleScope<1> hs(self);
+ Handle<MethodHandlesLookup> mhl(
+ hs.NewHandle(ObjPtr<MethodHandlesLookup>::DownCast(StaticClass()->AllocObject(self))));
+ mhl->SetFieldObject<false>(LookupClassOffset(), lookup_class.Get());
+ mhl->SetField32<false>(AllowedModesOffset(), kAllModes);
+ return mhl.Get();
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
new file mode 100644
index 0000000..63eb428
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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_METHOD_HANDLES_LOOKUP_H_
+#define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
+
+#include "obj_ptr.h"
+#include "gc_root.h"
+#include "object.h"
+#include "handle.h"
+#include "utils.h"
+
+namespace art {
+
+struct MethodHandlesLookupOffsets;
+class RootVisitor;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.MethodHandles.Lookup
+class MANAGED MethodHandlesLookup : public Object {
+ public:
+ static mirror::MethodHandlesLookup* Create(Thread* const self,
+ Handle<Class> lookup_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static MemberOffset AllowedModesOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, allowed_modes_));
+ }
+
+ static MemberOffset LookupClassOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, lookup_class_));
+ }
+
+ HeapReference<mirror::Class> lookup_class_;
+
+ int32_t allowed_modes_;
+
+ static GcRoot<mirror::Class> static_class_; // java.lang.invoke.MethodHandles.Lookup.class
+
+ friend struct art::MethodHandlesLookupOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 5d77a16..4b8dfac 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -44,6 +44,22 @@
return mt.Get();
}
+size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::ObjectArray<Class>* const p_types = GetPTypes();
+ const int32_t p_types_length = p_types->GetLength();
+
+ // Initialize |num_vregs| with number of parameters and only increment it for
+ // types requiring a second vreg.
+ size_t num_vregs = static_cast<size_t>(p_types_length);
+ for (int32_t i = 0; i < p_types_length; ++i) {
+ mirror::Class* klass = p_types->GetWithoutChecks(i);
+ if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
+ ++num_vregs;
+ }
+ }
+ return num_vregs;
+}
+
bool MethodType::IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::ObjectArray<Class>* const p_types = GetPTypes();
const int32_t params_length = p_types->GetLength();
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 9a98143..374bbe5 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -44,6 +44,10 @@
return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_));
}
+ // Number of virtual registers required to hold the parameters for
+ // this method type.
+ size_t NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_);
+
Class* GetRType() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_));
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 31eb1cc..528eddc 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -273,6 +273,36 @@
return true;
}
+static bool FindDexFileMapItem(const uint8_t* dex_begin,
+ const uint8_t* dex_end,
+ DexFile::MapItemType map_item_type,
+ const DexFile::MapItem** result_item) {
+ *result_item = nullptr;
+
+ const DexFile::Header* header =
+ BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
+ if (nullptr == header) return false;
+
+ if (!DexFile::IsMagicValid(header->magic_)) return true; // Not a dex file, not an error.
+
+ const DexFile::MapList* map_list =
+ BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
+ if (nullptr == map_list) return false;
+
+ const DexFile::MapItem* map_item = map_list->list_;
+ size_t count = map_list->size_;
+ while (count--) {
+ if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
+ *result_item = map_item;
+ break;
+ }
+ map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
+ if (nullptr == map_item) return false;
+ }
+
+ return true;
+}
+
bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -501,7 +531,19 @@
uint8_t* current_dex_cache_arrays = nullptr;
if (dex_cache_arrays != nullptr) {
- DexCacheArraysLayout layout(pointer_size, *header);
+ // All DexCache types except for CallSite have their instance counts in the
+ // DexFile header. For CallSites, we need to read the info from the MapList.
+ const DexFile::MapItem* call_sites_item = nullptr;
+ if (!FindDexFileMapItem(DexBegin(),
+ DexEnd(),
+ DexFile::MapItemType::kDexTypeCallSiteIdItem,
+ &call_sites_item)) {
+ *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
+ GetLocation().c_str());
+ return false;
+ }
+ size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
+ DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
if (layout.Size() != 0u) {
if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9609bee..f8f3d76 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -95,6 +95,7 @@
#include "mirror/field.h"
#include "mirror/method.h"
#include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/method_type.h"
#include "mirror/stack_trace_element.h"
#include "mirror/throwable.h"
@@ -1715,6 +1716,7 @@
mirror::Field::VisitRoots(visitor);
mirror::MethodType::VisitRoots(visitor);
mirror::MethodHandleImpl::VisitRoots(visitor);
+ mirror::MethodHandlesLookup::VisitRoots(visitor);
mirror::EmulatedStackFrame::VisitRoots(visitor);
mirror::ClassExt::VisitRoots(visitor);
// Visit all the primitive array types classes.
diff --git a/runtime/utils.h b/runtime/utils.h
index 67438b5..96e5bfa 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -301,6 +301,30 @@
}
}
+// Returns a type cast pointer if object pointed to is within the provided bounds.
+// Otherwise returns nullptr.
+template <typename T>
+inline static T BoundsCheckedCast(const void* pointer,
+ const void* lower,
+ const void* upper) {
+ const uint8_t* bound_begin = static_cast<const uint8_t*>(lower);
+ const uint8_t* bound_end = static_cast<const uint8_t*>(upper);
+ DCHECK(bound_begin <= bound_end);
+
+ T result = reinterpret_cast<T>(pointer);
+ const uint8_t* begin = static_cast<const uint8_t*>(pointer);
+ const uint8_t* end = begin + sizeof(*result);
+ if (begin < bound_begin || end > bound_end || begin > end) {
+ return nullptr;
+ }
+ return result;
+}
+
+template <typename T, size_t size>
+constexpr size_t ArrayCount(const T (&)[size]) {
+ return size;
+}
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index bd1b044..9865821 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -29,7 +29,8 @@
namespace art {
inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size,
- const DexFile::Header& header)
+ const DexFile::Header& header,
+ uint32_t num_call_sites)
: pointer_size_(pointer_size),
/* types_offset_ is always 0u, so it's constexpr */
methods_offset_(
@@ -40,12 +41,14 @@
RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())),
method_types_offset_(
RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), MethodTypesAlignment())),
- size_(
- RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) {
+ call_sites_offset_(
+ RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_),
+ MethodTypesAlignment())),
+ size_(RoundUp(call_sites_offset_ + CallSitesSize(num_call_sites), Alignment())) {
}
inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file)
- : DexCacheArraysLayout(pointer_size, dex_file->GetHeader()) {
+ : DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) {
}
inline constexpr size_t DexCacheArraysLayout::Alignment() {
@@ -131,10 +134,18 @@
inline size_t DexCacheArraysLayout::MethodTypesAlignment() const {
static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
- "alignof(MethodTypeDexCacheType) != 8");
+ "Expecting alignof(MethodTypeDexCacheType) == 8");
return alignof(mirror::MethodTypeDexCacheType);
}
+inline size_t DexCacheArraysLayout::CallSitesSize(size_t num_elements) const {
+ return ArraySize(GcRootAsPointerSize<mirror::CallSite>(), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::CallSitesAlignment() const {
+ return alignof(GcRoot<mirror::CallSite>);
+}
+
inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) {
return static_cast<size_t>(element_size) * idx;
}
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 7d4b23a..ed677ed 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -37,11 +37,14 @@
strings_offset_(0u),
fields_offset_(0u),
method_types_offset_(0u),
+ call_sites_offset_(0u),
size_(0u) {
}
// Construct a layout for a particular dex file header.
- DexCacheArraysLayout(PointerSize pointer_size, const DexFile::Header& header);
+ DexCacheArraysLayout(PointerSize pointer_size,
+ const DexFile::Header& header,
+ uint32_t num_call_sites);
// Construct a layout for a particular dex file.
DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file);
@@ -104,6 +107,14 @@
size_t MethodTypesAlignment() const;
+ size_t CallSitesOffset() const {
+ return call_sites_offset_;
+ }
+
+ size_t CallSitesSize(size_t num_elements) const;
+
+ size_t CallSitesAlignment() const;
+
private:
static constexpr size_t types_offset_ = 0u;
const PointerSize pointer_size_; // Must be first for construction initialization order.
@@ -111,6 +122,7 @@
const size_t strings_offset_;
const size_t fields_offset_;
const size_t method_types_offset_;
+ const size_t call_sites_offset_;
const size_t size_;
static size_t Alignment(PointerSize pointer_size);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 02f1e1b..634bd47 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -408,4 +408,23 @@
IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0])));
}
+TEST_F(UtilsTest, ArrayCount) {
+ int i[64];
+ EXPECT_EQ(ArrayCount(i), 64u);
+ char c[7];
+ EXPECT_EQ(ArrayCount(c), 7u);
+}
+
+TEST_F(UtilsTest, BoundsCheckedCast) {
+ char buffer[64];
+ const char* buffer_end = buffer + ArrayCount(buffer);
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
+ reinterpret_cast<const uint64_t*>(buffer));
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
+ reinterpret_cast<const uint64_t*>(buffer + 56));
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
+}
+
} // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5f55f3f..16739fa 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3114,6 +3114,44 @@
just_set_result = true;
break;
}
+ case Instruction::INVOKE_CUSTOM:
+ case Instruction::INVOKE_CUSTOM_RANGE: {
+ // Verify registers based on method_type in the call site.
+ bool is_range = (inst->Opcode() == Instruction::INVOKE_CUSTOM_RANGE);
+
+ // Step 1. Check the call site that produces the method handle for invocation
+ const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ if (!CheckCallSite(call_site_idx)) {
+ DCHECK(HasFailures());
+ break;
+ }
+
+ // Step 2. Check the register arguments correspond to the expected arguments for the
+ // method handle produced by step 1. The dex file verifier has checked ranges for
+ // the first three arguments and CheckCallSite has checked the method handle type.
+ CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+ it.Next(); // Skip to name.
+ it.Next(); // Skip to method type of the method handle
+ const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
+ DexFileParameterIterator param_it(*dex_file_, proto_id);
+ // Treat method as static as it has yet to be determined.
+ VerifyInvocationArgsFromIterator(¶m_it, inst, METHOD_STATIC, is_range, nullptr);
+ const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
+
+ // Step 3. Propagate return type information
+ const RegType& return_type =
+ reg_types_.FromDescriptor(GetClassLoader(), return_descriptor, false);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(this, return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
+ }
+ just_set_result = true;
+ // TODO: Add compiler support for invoke-custom (b/35337872).
+ Fail(VERIFY_ERROR_FORCE_INTERPRETER);
+ break;
+ }
case Instruction::NEG_INT:
case Instruction::NOT_INT:
work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer());
@@ -3423,7 +3461,7 @@
/* These should never appear during verification. */
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
- case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
@@ -4094,6 +4132,116 @@
VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, nullptr);
}
+bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) {
+ CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+ // Check essential arguments are provided. The dex file verifier has verified indicies of the
+ // main values (method handle, name, method_type).
+ if (it.Size() < 3) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " has too few arguments: "
+ << it.Size() << "< 3";
+ return false;
+ }
+
+ // Get and check the first argument: the method handle.
+ uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ it.Next();
+ const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
+ if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " argument 0 method handle type is not InvokeStatic";
+ return false;
+ }
+
+ // Skip the second argument, the name to resolve, as checked by the
+ // dex file verifier.
+ it.Next();
+
+ // Skip the third argument, the method type expected, as checked by
+ // the dex file verifier.
+ it.Next();
+
+ // Check the bootstrap method handle and remaining arguments.
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_);
+ uint32_t length;
+ const char* shorty = dex_file_->GetMethodShorty(method_id, &length);
+
+ if (it.Size() < length - 1) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " too few arguments for bootstrap method: "
+ << it.Size() << " < " << (length - 1);
+ return false;
+ }
+
+ // Check the return type and first 3 arguments are references
+ // (CallSite, Lookup, String, MethodType). If they are not of the
+ // expected types (or subtypes), it will trigger a
+ // WrongMethodTypeException during execution.
+ if (shorty[0] != 'L') {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " bootstrap return type is not a reference";
+ return false;
+ }
+
+ for (uint32_t i = 1; i < 4; ++i) {
+ if (shorty[i] != 'L') {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " bootstrap method argument " << (i - 1)
+ << " is not a reference";
+ return false;
+ }
+ }
+
+ // Check the optional arguments.
+ for (uint32_t i = 4; i < length; ++i, it.Next()) {
+ bool match = false;
+ switch (it.GetValueType()) {
+ case EncodedArrayValueIterator::ValueType::kBoolean:
+ case EncodedArrayValueIterator::ValueType::kByte:
+ case EncodedArrayValueIterator::ValueType::kShort:
+ case EncodedArrayValueIterator::ValueType::kChar:
+ case EncodedArrayValueIterator::ValueType::kInt:
+ // These all fit within one register and encoders do not seem
+ // too exacting on the encoding type they use (ie using
+ // integer for all of these).
+ match = (strchr("ZBCSI", shorty[i]) != nullptr);
+ break;
+ case EncodedArrayValueIterator::ValueType::kLong:
+ match = ('J' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kFloat:
+ match = ('F' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kDouble:
+ match = ('D' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kMethodType:
+ case EncodedArrayValueIterator::ValueType::kMethodHandle:
+ case EncodedArrayValueIterator::ValueType::kString:
+ case EncodedArrayValueIterator::ValueType::kType:
+ case EncodedArrayValueIterator::ValueType::kNull:
+ match = ('L' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kField:
+ case EncodedArrayValueIterator::ValueType::kMethod:
+ case EncodedArrayValueIterator::ValueType::kEnum:
+ case EncodedArrayValueIterator::ValueType::kArray:
+ case EncodedArrayValueIterator::ValueType::kAnnotation:
+ // Unreachable based on current EncodedArrayValueIterator::Next().
+ UNREACHABLE();
+ }
+
+ if (!match) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " bootstrap method argument " << (i - 1)
+ << " expected " << shorty[i]
+ << " got value type: " << it.GetValueType();
+ return false;
+ }
+ }
+ return true;
+}
+
class MethodParamListDescriptorIterator {
public:
explicit MethodParamListDescriptorIterator(ArtMethod* res_method) :
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index fa5a698..7b67967 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -697,6 +697,11 @@
REQUIRES_SHARED(Locks::mutator_lock_);
/*
+ * Verify the arguments present for a call site. Returns "true" if all is well, "false" otherwise.
+ */
+ bool CheckCallSite(uint32_t call_site_idx);
+
+ /*
* Verify that the target instruction is not "move-exception". It's important that the only way
* to execute a move-exception is as the first instruction of an exception handler.
* Returns "true" if all is well, "false" if the target instruction is move-exception.