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