blob: 1e49886b9525812c4df42e0dc7b56e6f76a24449 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lambda/box_class_table.h"
#include "base/mutex.h"
#include "common_throws.h"
#include "gc_root-inl.h"
#include "lambda/closure.h"
#include "lambda/leaking_allocator.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "thread.h"
#include <string>
#include <vector>
namespace art {
namespace lambda {
// Create the lambda proxy class given the name of the lambda interface (e.g. Ljava/lang/Runnable;)
// Also needs a proper class loader (or null for bootclasspath) where the proxy will be created
// into.
//
// The class must **not** have already been created.
// Returns a non-null ptr on success, otherwise returns null and has an exception set.
static mirror::Class* CreateClass(Thread* self,
const std::string& class_name,
const Handle<mirror::ClassLoader>& class_loader)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedObjectAccessUnchecked soa(self);
StackHandleScope<2> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// Find the java.lang.Class for our class name (from the class loader).
Handle<mirror::Class> lambda_interface =
hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), class_loader));
// TODO: use LookupClass in a loop
// TODO: DCHECK That this doesn't actually cause the class to be loaded,
// since the create-lambda should've loaded it already
DCHECK(lambda_interface.Get() != nullptr) << "CreateClass with class_name=" << class_name;
DCHECK(lambda_interface->IsInterface()) << "CreateClass with class_name=" << class_name;
jobject lambda_interface_class = soa.AddLocalReference<jobject>(lambda_interface.Get());
// Look up java.lang.reflect.Proxy#getLambdaProxyClass method.
Handle<mirror::Class> java_lang_reflect_proxy =
hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/reflect/Proxy;"));
jclass java_lang_reflect_proxy_class =
soa.AddLocalReference<jclass>(java_lang_reflect_proxy.Get());
DCHECK(java_lang_reflect_proxy.Get() != nullptr);
jmethodID proxy_factory_method_id =
soa.Env()->GetStaticMethodID(java_lang_reflect_proxy_class,
"getLambdaProxyClass",
"(Ljava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;");
DCHECK(!soa.Env()->ExceptionCheck());
// Call into the java code to do the hard work of figuring out which methods and throws
// our lambda interface proxy needs to implement. It then calls back into the class linker
// on our behalf to make the proxy itself.
jobject generated_lambda_proxy_class =
soa.Env()->CallStaticObjectMethod(java_lang_reflect_proxy_class,
proxy_factory_method_id,
class_loader.ToJObject(),
lambda_interface_class);
// This can throw in which case we return null. Caller must handle.
return soa.Decode<mirror::Class*>(generated_lambda_proxy_class);
}
BoxClassTable::BoxClassTable() {
}
BoxClassTable::~BoxClassTable() {
// Don't need to do anything, classes are deleted automatically by GC
// when the classloader is deleted.
//
// Our table will not outlive the classloader since the classloader owns it.
}
mirror::Class* BoxClassTable::GetOrCreateBoxClass(const char* class_name,
const Handle<mirror::ClassLoader>& class_loader) {
DCHECK(class_name != nullptr);
Thread* self = Thread::Current();
std::string class_name_str = class_name;
{
MutexLock mu(self, *Locks::lambda_class_table_lock_);
// Attempt to look up this class, it's possible it was already created previously.
// If this is the case we *must* return the same class as before to maintain
// referential equality between box instances.
//
// In managed code:
// Functional f = () -> 5; // vF = create-lambda
// Object a = f; // vA = box-lambda vA
// Object b = f; // vB = box-lambda vB
// assert(a.getClass() == b.getClass())
// assert(a == b)
ValueType value = FindBoxedClass(class_name_str);
if (!value.IsNull()) {
return value.Read();
}
}
// Otherwise we need to generate a class ourselves and insert it into the hash map
// Release the table lock here, which implicitly allows other threads to suspend
// (since the GC callbacks will not block on trying to acquire our lock).
// We also don't want to call into the class linker with the lock held because
// our lock level is lower.
self->AllowThreadSuspension();
// Create a lambda proxy class, within the specified class loader.
mirror::Class* lambda_proxy_class = CreateClass(self, class_name_str, class_loader);
// There are no thread suspension points after this, so we don't need to put it into a handle.
ScopedAssertNoThreadSuspension soants{self, "BoxClassTable::GetOrCreateBoxClass"}; // NOLINT: [readability/braces] [4]
if (UNLIKELY(lambda_proxy_class == nullptr)) {
// Most likely an OOM has occurred.
CHECK(self->IsExceptionPending());
return nullptr;
}
{
MutexLock mu(self, *Locks::lambda_class_table_lock_);
// Possible, but unlikely, that someone already came in and made a proxy class
// on another thread.
ValueType value = FindBoxedClass(class_name_str);
if (UNLIKELY(!value.IsNull())) {
DCHECK_EQ(lambda_proxy_class, value.Read());
return value.Read();
}
// Otherwise we made a brand new proxy class.
// The class itself is cleaned up by the GC (e.g. class unloading) later.
// Actually insert into the table.
map_.Insert({std::move(class_name_str), ValueType(lambda_proxy_class)});
}
return lambda_proxy_class;
}
BoxClassTable::ValueType BoxClassTable::FindBoxedClass(const std::string& class_name) const {
auto map_iterator = map_.Find(class_name);
if (map_iterator != map_.end()) {
const std::pair<UnorderedMapKeyType, ValueType>& key_value_pair = *map_iterator;
const ValueType& value = key_value_pair.second;
DCHECK(!value.IsNull()); // Never store null boxes.
return value;
}
return ValueType(nullptr);
}
void BoxClassTable::EmptyFn::MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const {
item.first.clear();
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
item.second = ValueType(); // Also clear the GC root.
}
bool BoxClassTable::EmptyFn::IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const {
bool is_empty = item.first.empty();
DCHECK_EQ(item.second.IsNull(), is_empty);
return is_empty;
}
bool BoxClassTable::EqualsFn::operator()(const UnorderedMapKeyType& lhs,
const UnorderedMapKeyType& rhs) const {
// Be damn sure the classes don't just move around from under us.
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
// Being the same class name isn't enough, must also have the same class loader.
// When we are in the same class loader, classes are equal via the pointer.
return lhs == rhs;
}
size_t BoxClassTable::HashFn::operator()(const UnorderedMapKeyType& key) const {
return std::hash<std::string>()(key);
}
} // namespace lambda
} // namespace art