summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc316
1 files changed, 307 insertions, 9 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2dd2a83888..8a0d8d4522 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -56,6 +56,7 @@
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "lambda/box_class_table.h"
#include "leb128.h"
#include "linear_alloc.h"
#include "mirror/class.h"
@@ -64,6 +65,7 @@
#include "mirror/dex_cache-inl.h"
#include "mirror/field.h"
#include "mirror/iftable-inl.h"
+#include "mirror/lambda_proxy.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
@@ -581,6 +583,9 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
// Create java.lang.reflect.Proxy root.
SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
+ // Create java.lang.LambdaProxy root.
+ SetClassRoot(kJavaLangLambdaProxy, FindSystemClass(self, "Ljava/lang/LambdaProxy;"));
+
// Create java.lang.reflect.Field.class root.
auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
CHECK(class_root != nullptr);
@@ -1257,6 +1262,7 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) {
}
delete data.allocator;
delete data.class_table;
+ delete data.lambda_box_class_table;
}
mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
@@ -1898,8 +1904,10 @@ const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool*
// Special case to get oat code without overwriting a trampoline.
const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
CHECK(method->IsInvokable()) << PrettyMethod(method);
- if (method->IsProxyMethod()) {
+ if (method->IsReflectProxyMethod()) {
return GetQuickProxyInvokeHandler();
+ } else if (method->IsLambdaProxyMethod()) {
+ return GetQuickLambdaProxyInvokeHandler();
}
bool found;
OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
@@ -3257,7 +3265,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
klass->SetName(soa.Decode<mirror::String*>(name));
klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
- std::string descriptor(GetDescriptorForProxy(klass.Get()));
+ std::string descriptor(GetDescriptorForAnyProxy(klass.Get()));
const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
// Needs to be before we insert the class so that the allocator field is set.
@@ -3377,23 +3385,228 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
decoded_name->ToModifiedUtf8().c_str()));
CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
- CHECK_EQ(klass.Get()->GetInterfaces(),
+ CHECK_EQ(klass.Get()->GetInterfacesForAnyProxy(),
+ soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+ CHECK_EQ(klass.Get()->GetThrowsForAnyProxy(),
+ soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
+ }
+ return klass.Get();
+}
+
+mirror::Class* ClassLinker::CreateLambdaProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+ jstring name,
+ jobjectArray interfaces,
+ jobject loader,
+ jobjectArray methods,
+ jobjectArray throws,
+ bool* already_exists) {
+ DCHECK(already_exists != nullptr);
+ *already_exists = false;
+
+ Thread* self = soa.Self();
+ StackHandleScope<10> hs(self);
+
+ // Allocate a new java.lang.Class object for a mirror::Proxy.
+ MutableHandle<mirror::Class> klass =
+ hs.NewHandle(AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)));
+ if (klass.Get() == nullptr) {
+ CHECK(self->IsExceptionPending()); // OOME.
+ return nullptr;
+ }
+ DCHECK(klass->GetClass() != nullptr);
+ klass->SetObjectSize(sizeof(mirror::LambdaProxy));
+
+ // Set the class access flags incl. preverified, so we do not try to set the flag on the methods.
+ klass->SetAccessFlags(kAccClassIsLambdaProxy | kAccPublic | kAccFinal | kAccPreverified);
+ klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader));
+ DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
+ klass->SetName(soa.Decode<mirror::String*>(name));
+ klass->SetDexCache(GetClassRoot(kJavaLangLambdaProxy)->GetDexCache());
+ // Set the status to be just before after loading it, but before anything is resolved.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
+ // Convert "foo.bar.baz" string to "Lfoo/bar/baz;"
+ std::string type_descriptor(GetDescriptorForAnyProxy(klass.Get()));
+
+ mirror::Class* existing;
+ {
+ const size_t hash = ComputeModifiedUtf8Hash(type_descriptor.c_str());
+
+ // Insert the class before loading the fields as the field roots
+ // (ArtField::declaring_class_) are only visited from the class
+ // table. There can't be any suspend points between inserting the
+ // class and setting the field arrays below.
+ existing = InsertClass(type_descriptor.c_str(), klass.Get(), hash);
+ }
+ if (UNLIKELY(existing != nullptr)) {
+ // We had already made the lambda proxy previously. Return it.
+
+ *already_exists = true;
+ return existing;
+ // Let the GC clean up the class we had already allocated but isn't being used.
+ }
+
+ // Needs to be after we insert the class so that the allocator field is set.
+ LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(klass->GetClassLoader());
+
+ // Instance fields are inherited, but we add a couple of static fields...
+ LengthPrefixedArray<ArtField>* sfields =
+ AllocArtFieldArray(self, allocator, mirror::LambdaProxy::kStaticFieldCount);
+ klass->SetSFieldsPtr(sfields);
+
+ // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by
+ // our proxy, so Class.getInterfaces doesn't return the flattened set.
+ // -- private static java.lang.Class[] interfaces; // list of declared interfaces
+ ArtField& interfaces_sfield = sfields->At(mirror::LambdaProxy::kStaticFieldIndexInterfaces);
+ interfaces_sfield.SetDexFieldIndex(mirror::LambdaProxy::kStaticFieldIndexInterfaces);
+ interfaces_sfield.SetDeclaringClass(klass.Get());
+ interfaces_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+
+ // 2. Create a static field 'throws' that holds the classes of exceptions thrown by our methods.
+ // This is returned by java.lang.reflect.Method#getExceptionTypes()
+ // --- private static java.lang.Class[][] throws; // maps vtable id to list of classes.
+ ArtField& throws_sfield = sfields->At(mirror::LambdaProxy::kStaticFieldIndexThrows);
+ throws_sfield.SetDexFieldIndex(mirror::LambdaProxy::kStaticFieldIndexThrows);
+ throws_sfield.SetDeclaringClass(klass.Get());
+ throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+
+ // Set up the Constructor method.
+ {
+ // Lambda proxies have 1 direct method, the constructor.
+ static constexpr size_t kNumDirectMethods = 1;
+ LengthPrefixedArray<ArtMethod>* directs = AllocArtMethodArray(self,
+ allocator,
+ kNumDirectMethods);
+ // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we
+ // want to throw OOM in the future.
+ if (UNLIKELY(directs == nullptr)) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ klass->SetDirectMethodsPtr(directs);
+ CreateLambdaProxyConstructor(klass, klass->GetDirectMethodUnchecked(0, image_pointer_size_));
+ }
+
+ // Create virtual method using specified prototypes.
+ auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods));
+ DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass())
+ << PrettyClass(h_methods->GetClass());
+ const size_t num_virtual_methods = h_methods->GetLength();
+ auto* virtuals = AllocArtMethodArray(self, allocator, num_virtual_methods);
+ // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we
+ // want to throw OOM in the future.
+ if (UNLIKELY(virtuals == nullptr)) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ klass->SetVirtualMethodsPtr(virtuals);
+ size_t abstract_methods = 0;
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ ArtMethod* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
+ ArtMethod* prototype = h_methods->Get(i)->GetArtMethod();
+ if (UNLIKELY((prototype->GetAccessFlags() & kAccDefault) != 0)) {
+ UNIMPLEMENTED(FATAL) << "Lambda proxies don't support default methods yet";
+ }
+ if (prototype->IsAbstract()) {
+ abstract_methods++;
+ }
+ VLOG(class_linker) << "Creating lambda proxy method for " << PrettyMethod(prototype);
+
+ CreateLambdaProxyMethod(klass, prototype, virtual_method);
+ DCHECK(virtual_method->GetDeclaringClass() != nullptr);
+ DCHECK(prototype->GetDeclaringClass() != nullptr);
+ }
+ // Ignore any methods from Object and default methods, it doesn't matter.
+ // Sanity check that the prototype interface is indeed compatible with lambdas.
+ DCHECK_EQ(abstract_methods, 1u)
+ << "Interface must be a single-abstract-method type" << PrettyClass(klass.Get());
+
+ // The super class is java.lang.LambdaProxy
+ klass->SetSuperClass(GetClassRoot(kJavaLangLambdaProxy));
+ // Now effectively in the loaded state.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, self);
+ self->AssertNoPendingException();
+
+ MutableHandle<mirror::Class> new_class = hs.NewHandle<mirror::Class>(nullptr);
+ {
+ // Must hold lock on object when resolved.
+ ObjectLock<mirror::Class> resolution_lock(self, klass);
+ // Link the fields and virtual methods, creating vtable and iftables.
+ // The new class will replace the old one in the class table.
+ Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)));
+
+ {
+ DCHECK_EQ(1, h_interfaces->GetLength()) << "Lambda proxies must implement 1 interface only";
+ mirror::Class* single_abstract_interface = h_interfaces->Get(0);
+ DCHECK(single_abstract_interface != nullptr);
+
+ // Use the dex cache from the interface, which will enable most of the
+ // dex-using mechanisms on the class and its methods will work.
+ klass->SetDexCache(single_abstract_interface->GetDexCache());
+ }
+
+ if (!LinkClass(self, type_descriptor.c_str(), klass, h_interfaces, &new_class)) {
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ return nullptr;
+ }
+ }
+ CHECK(klass->IsRetired());
+ CHECK_NE(klass.Get(), new_class.Get());
+ klass.Assign(new_class.Get());
+
+ CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get());
+ interfaces_sfield.SetObject<false>(klass.Get(),
+ soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+
+ CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get());
+ throws_sfield.SetObject<false>(
+ klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+
+ {
+ // Lock on klass is released. Lock new class object.
+ ObjectLock<mirror::Class> initialization_lock(self, klass);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self);
+ }
+
+ // Sanity checks
+ if (kIsDebugBuild) {
+ CHECK(klass->GetIFieldsPtr() == nullptr);
+ CheckLambdaProxyConstructor(klass->GetDirectMethod(0, image_pointer_size_));
+
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ ArtMethod* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
+ ArtMethod* prototype = h_methods->Get(i++)->GetArtMethod();
+ CheckLambdaProxyMethod(virtual_method, prototype);
+ }
+
+ StackHandleScope<1> hs2(self);
+ Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String*>(name));
+ std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces",
+ decoded_name->ToModifiedUtf8().c_str()));
+ CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name);
+
+ std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws",
+ decoded_name->ToModifiedUtf8().c_str()));
+ CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
+
+ CHECK_EQ(klass.Get()->GetInterfacesForAnyProxy(),
soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
- CHECK_EQ(klass.Get()->GetThrows(),
+ CHECK_EQ(klass.Get()->GetThrowsForAnyProxy(),
soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
}
return klass.Get();
}
-std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) {
- DCHECK(proxy_class->IsProxyClass());
+std::string ClassLinker::GetDescriptorForAnyProxy(mirror::Class* proxy_class) {
+ DCHECK(proxy_class != nullptr);
+ DCHECK(proxy_class->IsAnyProxyClass());
mirror::String* name = proxy_class->GetName();
DCHECK(name != nullptr);
return DotToDescriptor(name->ToModifiedUtf8().c_str());
}
ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) {
- DCHECK(proxy_class->IsProxyClass());
+ DCHECK(proxy_class->IsAnyProxyClass());
DCHECK(proxy_method->IsProxyMethod());
{
Thread* const self = Thread::Current();
@@ -3421,7 +3634,7 @@ ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
- CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 16u);
+ CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
2, image_pointer_size_);
// Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
@@ -3437,6 +3650,38 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod*
out->SetDeclaringClass(klass.Get());
}
+void ClassLinker::CreateLambdaProxyConstructor(Handle<mirror::Class> klass,
+ /*out*/ArtMethod* method_constructor) {
+ DCHECK(klass.Get() != nullptr);
+ DCHECK(method_constructor != nullptr);
+
+ // Create constructor for Proxy that must initialize the method.
+ // Lambda proxy superclass only has 1 direct method, the constructor (<init>()V)
+ CHECK_EQ(GetClassRoot(kJavaLangLambdaProxy)->NumDirectMethods(),
+ mirror::LambdaProxy::kDirectMethodCount);
+ // Get the constructor method.
+ ArtMethod* proxy_constructor = GetClassRoot(kJavaLangLambdaProxy)->GetDirectMethodUnchecked(
+ mirror::LambdaProxy::kDirectMethodIndexConstructor,
+ image_pointer_size_);
+
+ // Verify constructor method is indeed a constructor.
+ CHECK(proxy_constructor != nullptr);
+
+ // Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
+ // constructor method.
+ GetClassRoot(kJavaLangLambdaProxy)->GetDexCache()->SetResolvedMethod(
+ proxy_constructor->GetDexMethodIndex(),
+ proxy_constructor,
+ image_pointer_size_);
+
+ // Clone the existing constructor of LambdaProxy
+ // (our constructor would just invoke it so steal its code_ too).
+ method_constructor->CopyFrom(proxy_constructor, image_pointer_size_);
+ // Make this constructor public and fix the class to be our LambdaProxy version
+ method_constructor->SetAccessFlags((method_constructor->GetAccessFlags() & ~kAccProtected) | kAccPublic);
+ method_constructor->SetDeclaringClass(klass.Get());
+}
+
void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const {
CHECK(constructor->IsConstructor());
auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_);
@@ -3445,6 +3690,14 @@ void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const {
DCHECK(constructor->IsPublic());
}
+void ClassLinker::CheckLambdaProxyConstructor(ArtMethod* constructor) const {
+ CHECK(constructor->IsConstructor());
+ auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_);
+ CHECK_STREQ(np->GetName(), "<init>");
+ CHECK_STREQ(np->GetSignature().ToString().c_str(), "()V");
+ DCHECK(constructor->IsPublic());
+}
+
void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype,
ArtMethod* out) {
// Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
@@ -3456,6 +3709,7 @@ void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prot
dex_cache->SetResolvedMethod(
prototype->GetDexMethodIndex(), prototype, image_pointer_size_);
}
+
// We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize
// as necessary
DCHECK(out != nullptr);
@@ -3471,6 +3725,42 @@ void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prot
out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
}
+void ClassLinker::CreateLambdaProxyMethod(Handle<mirror::Class> klass,
+ ArtMethod* prototype,
+ ArtMethod* out) {
+ DCHECK(prototype != nullptr);
+ DCHECK(out != nullptr);
+
+ // DO NOT go through the proxy invoke handler for the default methods. They have no idea
+ // how to handle the raw closure, so they must get the regular object when invoked.
+ CHECK_EQ(prototype->GetAccessFlags() & kAccDefault, 0u) << "Default methods must not be proxied";
+
+ // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
+ // prototype method
+ auto* dex_cache = prototype->GetDeclaringClass()->GetDexCache();
+ // Avoid dirtying the dex cache unless we need to.
+ if (dex_cache->GetResolvedMethod(prototype->GetDexMethodIndex(), image_pointer_size_) !=
+ prototype) {
+ dex_cache->SetResolvedMethod(
+ prototype->GetDexMethodIndex(), prototype, image_pointer_size_);
+ }
+ // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize
+ // as necessary
+ out->CopyFrom(prototype, image_pointer_size_);
+
+ // Set class to be the concrete proxy class and clear the abstract flag, modify exceptions to
+ // the intersection of throw exceptions as defined in Proxy
+ out->SetDeclaringClass(klass.Get());
+ out->SetAccessFlags((out->GetAccessFlags() & ~kAccAbstract) | kAccFinal);
+
+ // Setting the entry point isn't safe for AOT since ASLR loads it anywhere at runtime.
+ CHECK(!Runtime::Current()->IsAotCompiler());
+
+ // At runtime the method looks like a reference and argument saving method, clone the code
+ // related parameters from this method.
+ out->SetEntryPointFromQuickCompiledCode(GetQuickLambdaProxyInvokeHandler());
+}
+
void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
// Basic sanity
CHECK(!prototype->IsFinal());
@@ -3492,6 +3782,11 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons
prototype->GetReturnType(true /* resolve */, image_pointer_size_));
}
+void ClassLinker::CheckLambdaProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
+ // same as above.
+ return CheckProxyMethod(method, prototype);
+}
+
bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
bool can_init_parents) {
if (can_init_statics && can_init_parents) {
@@ -4123,7 +4418,9 @@ ClassTable* ClassLinker::InsertClassTableForClassLoader(mirror::ClassLoader* cla
class_loader->SetClassTable(data.class_table);
// Should have been set when we registered the dex file.
data.allocator = class_loader->GetAllocator();
- CHECK(data.allocator != nullptr);
+ CHECK(class_loader->GetLambdaProxyCache() == nullptr);
+ data.lambda_box_class_table = new lambda::BoxClassTable();
+ class_loader->SetLambdaProxyCache(data.lambda_box_class_table);
class_loaders_.push_back(data);
}
return class_table;
@@ -6566,6 +6863,7 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
"Ljava/lang/reflect/Field;",
"Ljava/lang/reflect/Method;",
"Ljava/lang/reflect/Proxy;",
+ "Ljava/lang/LambdaProxy;",
"[Ljava/lang/String;",
"[Ljava/lang/reflect/Constructor;",
"[Ljava/lang/reflect/Field;",