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