runtime: Add lambda box/unbox object equality

A lambda that is boxed with box-lambda is now stored as a weak reference
in a global runtime table (lambda::BoxTable). Repeatedly boxing the same
lambda closure value will always return the same java.lang.Object back.

Since there is no way to observe the address of an object, a GC can
happen and clean up the table of any dead boxed lambdas, which can also
shrink the table to prevent the memory use from growing too much.

(Note that a lambda closure is immutable, so hashing over it is
guaranteed safe.)

Change-Id: I786c1323ff14eed937936b303d511875f9642524
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 776b6a3..9babb18 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -34,6 +34,7 @@
 #include "dex_instruction-inl.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "handle_scope-inl.h"
+#include "lambda/box_table.h"
 #include "mirror/class-inl.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
@@ -506,8 +507,8 @@
   uint32_t vreg_target_object = inst->VRegA_22x(inst_data);
   uint32_t vreg_source_closure = inst->VRegB_22x();
 
-  ArtMethod* const closure_method = ReadLambdaClosureFromVRegsOrThrow(shadow_frame,
-                                                                      vreg_source_closure);
+  ArtMethod* closure_method = ReadLambdaClosureFromVRegsOrThrow(shadow_frame,
+                                                                vreg_source_closure);
 
   // Failed lambda target runtime check, an exception was raised.
   if (UNLIKELY(closure_method == nullptr)) {
@@ -515,28 +516,21 @@
     return false;
   }
 
-  // Convert the ArtMethod into a java.lang.reflect.Method which will serve
-  // as the temporary 'boxed' version of the lambda. This is good enough
-  // to check all the basic object identities that a boxed lambda must retain.
+  mirror::Object* closure_as_object =
+      Runtime::Current()->GetLambdaBoxTable()->BoxLambda(closure_method);
 
-  // TODO: Boxing an innate lambda (i.e. made with create-lambda) should make a proxy class
-  // TODO: Boxing a learned lambda (i.e. made with unbox-lambda) should return the original object
-  // TODO: Repeated boxing should return the same object reference
-  mirror::Method* method_as_object =
-      mirror::Method::CreateFromArtMethod(self, closure_method);
-
-  if (UNLIKELY(method_as_object == nullptr)) {
-    // Most likely an OOM has occurred.
+  // Failed to box the lambda, an exception was raised.
+  if (UNLIKELY(closure_as_object == nullptr)) {
     CHECK(self->IsExceptionPending());
     return false;
   }
 
-  shadow_frame.SetVRegReference(vreg_target_object, method_as_object);
+  shadow_frame.SetVRegReference(vreg_target_object, closure_as_object);
   return true;
 }
 
 template <bool _do_check> SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-static inline bool DoUnboxLambda(Thread* self ATTRIBUTE_UNUSED,
+static inline bool DoUnboxLambda(Thread* self,
                                  ShadowFrame& shadow_frame,
                                  const Instruction* inst,
                                  uint16_t inst_data) {
@@ -556,23 +550,15 @@
     return false;
   }
 
-  // Raise ClassCastException if object is not instanceof java.lang.reflect.Method
-  if (UNLIKELY(!boxed_closure_object->InstanceOf(mirror::Method::StaticClass()))) {
-    ThrowClassCastException(mirror::Method::StaticClass(), boxed_closure_object->GetClass());
+  ArtMethod* unboxed_closure = nullptr;
+  // Raise an exception if unboxing fails.
+  if (!Runtime::Current()->GetLambdaBoxTable()->UnboxLambda(boxed_closure_object,
+                                                            &unboxed_closure)) {
+    CHECK(self->IsExceptionPending());
     return false;
   }
 
-  // TODO(iam): We must check that the closure object extends/implements the type
-  // specified in [type id]. This is not currently implemented since it's always a Method.
-
-  // If we got this far, the inputs are valid.
-  // Write out the java.lang.reflect.Method's embedded ArtMethod* into the vreg target.
-  mirror::AbstractMethod* boxed_closure_as_method =
-      down_cast<mirror::AbstractMethod*>(boxed_closure_object);
-
-  ArtMethod* unboxed_closure = boxed_closure_as_method->GetArtMethod();
   DCHECK(unboxed_closure != nullptr);
-
   WriteLambdaClosureIntoVRegs(shadow_frame, *unboxed_closure, vreg_target_closure);
   return true;
 }