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