Merge "lambda: Add support for invoke-interface for boxed innate lambdas"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index dcde5ab..717403f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -29,6 +29,7 @@
GetMethodSignature \
Instrumentation \
Interfaces \
+ LambdaInterfaces \
Lookup \
Main \
MultiDex \
@@ -77,6 +78,7 @@
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_proxy_test_DEX_DEPS := Interfaces
+ART_GTEST_lambda_proxy_test_DEX_DEPS := LambdaInterfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
@@ -97,6 +99,7 @@
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_lambda_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
# The dexdump test requires an image and the dexdump utility.
# TODO: rename into dexdump when migration completes
@@ -233,6 +236,7 @@
COMPILER_GTEST_COMMON_SRC_FILES := \
runtime/jni_internal_test.cc \
+ runtime/lambda_proxy_test.cc \
runtime/proxy_test.cc \
runtime/reflection_test.cc \
compiler/compiled_method_test.cc \
@@ -741,6 +745,7 @@
ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
ART_GTEST_object_test_DEX_DEPS :=
ART_GTEST_proxy_test_DEX_DEPS :=
+ART_GTEST_lambda_proxy_test_DEX_DEPS :=
ART_GTEST_reflection_test_DEX_DEPS :=
ART_GTEST_stub_test_DEX_DEPS :=
ART_GTEST_transaction_test_DEX_DEPS :=
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 0b0f094..4f4792a 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -106,6 +106,7 @@
jit/profiling_info.cc \
lambda/art_lambda_method.cc \
lambda/box_table.cc \
+ lambda/box_class_table.cc \
lambda/closure.cc \
lambda/closure_builder.cc \
lambda/leaking_allocator.cc \
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index d6ba304..771c8b7 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -46,9 +46,15 @@
}
};
+} // namespace art
+
// Common tests are declared next to the constants.
#define ADD_TEST_EQ(x, y) EXPECT_EQ(x, y);
#include "asm_support.h"
+// Important: Do not include this inside of another namespace, since asm_support.h
+// defines its own namespace which must not be nested.
+
+namespace art {
TEST_F(ArchTest, CheckCommonOffsetsAndSizes) {
CheckAsmSupportOffsetsAndSizes();
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 631b784..588268d 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1045,6 +1045,26 @@
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+ .extern artQuickLambdaProxyInvokeHandler
+ENTRY art_quick_lambda_proxy_invoke_handler
+// TODO: have a faster handler that doesn't need to set up a frame
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ blx artQuickLambdaProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP)
+ ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
+ // Tear down the callee-save frame. Skip arg registers.
+ add sp, #(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+ .cfi_adjust_cfa_offset -(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ cbnz r2, 1f @ success if no exception is pending
+ vmov d0, r0, r1 @ store into fpr, for when it's a fpr return...
+ bx lr @ return on success
+1:
+ DELIVER_PENDING_EXCEPTION
+END art_quick_lambda_proxy_invoke_handler
+
/*
* Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
* dex method index.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 9ccabad..177873d 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1582,6 +1582,28 @@
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+ /*
+ * Called by managed code that is attempting to call a method on a lambda proxy class. On entry
+ * x0 holds the lambda proxy method and x1 holds the receiver; The frame size of the invoked
+ * lambda proxy method agrees with a ref and args callee save frame.
+ */
+ .extern artQuickLambdaProxyInvokeHandler
+ENTRY art_quick_lambda_proxy_invoke_handler
+// TODO: have a faster way to invoke lambda proxies without setting up the whole frame.
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0
+ mov x2, xSELF // pass Thread::Current
+ mov x3, sp // pass SP
+ bl artQuickLambdaProxyInvokeHandler // (Method* proxy method, receiver, Thread*, SP)
+ ldr x2, [xSELF, THREAD_EXCEPTION_OFFSET]
+ cbnz x2, .Lexception_in_lambda_proxy // success if no exception is pending
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame
+ fmov d0, x0 // Store result in d0 in case it was float or double
+ ret // return on success
+.Lexception_in_lambda_proxy:
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ DELIVER_PENDING_EXCEPTION
+END art_quick_lambda_proxy_invoke_handler
+
/*
* Called to resolve an imt conflict. xIP1 is a hidden argument that holds the target method's
* dex method index.
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 0691f2a..af79f5e 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1377,6 +1377,10 @@
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+ .extern artQuickLambdaProxyInvokeHandler
+UNIMPLEMENTED art_quick_lambda_proxy_invoke_handler
+
/*
* Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's
* dex method index.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 66c8aad..5e70a95 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1431,6 +1431,10 @@
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+ .extern artQuickLambdaProxyInvokeHandler
+UNIMPLEMENTED art_quick_lambda_proxy_invoke_handler
+
/*
* Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's
* dex method index.
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 463c9cf..4fb6119 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1391,6 +1391,149 @@
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_proxy_invoke_handler
+#if LAMBDA_INVOKE_USES_LONG
+#undef LAMBDA_PROXY_SETUP_FRAME
+// We need to always do a 'pop' to readjust the stack, so we have to use the slower call instruction.
+#define LAMBDA_PROXY_SETUP_FRAME 1
+#define LAMBDA_INVOKE_REALIGN_STACK_FRAME 1
+#else
+#define LAMBDA_INVOKE_REALIGN_STACK_FRAME 0
+#endif
+
+#define LAMBDA_INVOKE_CALLS_INTO_RUNTIME LAMBDA_INVOKE_REALIGN_STACK_FRAME
+
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+DEFINE_FUNCTION art_quick_lambda_proxy_invoke_handler
+ // This function is always called when the lambda is innate.
+ // Therefore we can assume the box is to an innate lambda.
+ // TODO: perhaps there should be a DCHECK to make sure it's innate?
+
+#if LAMBDA_PROXY_SETUP_FRAME
+ // Set up a quick frame when debugging so we can see that it's going through a stub.
+ // An invoke-virtual + a stub invocation is enough of a hint that we *could* be
+ // going through a lambda proxy.
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX
+#endif
+
+#if !LAMBDA_INVOKE_CALLS_INTO_RUNTIME
+ // Rewrite the following 2 arguments, stored on stack frame:
+ //
+ // |--------|
+ // |receiver| <- esp-4
+ // |--------|
+ // | method | <- esp
+ // |--------|
+
+ // Set up the new correct method receiver (swap object with closure).
+ // -- The original object is no longer available after this.
+ //
+ // (Before)
+ // ecx == mirror::Object* boxed_lambda; // lambda proxy object.
+ movl MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET(%ecx), %ecx
+ // (After)
+ // lambda::Closure* closure = boxed_lambda->closure_;
+ // boxed_lambda = closure; // Overwrite lambda proxy object
+ // ecx == closure
+
+ // Look up the new correct method target.
+ // -- The original method target is no longer available after this.
+ //
+ // (Before)
+ // eax == ArtMethod* old_receiver_method;
+ movl LAMBDA_CLOSURE_METHOD_OFFSET(%ecx), %eax
+ // (After)
+ // ArtLambdaMethod* lambda_method_target = closure->lambda_info_;
+ // eax = lambda_method_target
+ //
+ // Set up the correct method target from the lambda info.
+ movl ART_LAMBDA_METHOD_ART_METHOD_OFFSET(%eax), %eax // Load new receiver method
+ // (After)
+ // ArtMethod* target_method = lambda_method_target->target_
+ // eax = target_method
+#endif
+
+#if LAMBDA_INVOKE_CALLS_INTO_RUNTIME
+ PUSH esp // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH ecx // pass receiver
+ PUSH eax // pass proxy method
+ call SYMBOL(artQuickLambdaProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+ movd %eax, %xmm0 // place return value also into floating point return value
+ movd %edx, %xmm1
+ punpckldq %xmm1, %xmm0
+ addl LITERAL(16 + FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE), %esp
+ CFI_ADJUST_CFA_OFFSET(-(16 + FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE))
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
+#endif
+
+#if LAMBDA_INVOKE_USES_LONG && !LAMBDA_INVOKE_REALIGN_STACK_FRAME
+ // As a temporary workaround, lambda functions look like
+ // (J[Arg2][Arg3][Arg4]...)
+ // This means that we can't just pass in the lambda as a 32-bit pointer
+ // We pad the arguments with an extra 32-bit "0" where Arg2 used to be instead.
+
+ // Required arguments for a lambda method:
+ //
+ // Arg0 = eax = method
+ // Arg1 = ecx = closure (hi)
+ // Arg2 = edx = closure (lo)
+ // Arg3 = ebx = <?> (first user-defined argument)
+
+ // Transformation diagram:
+ //
+ // Arg0 Arg1 Arg2 Arg3 ... ArgN
+ // | | \ \ \
+ // | | \ \ \
+ // Arg0 Arg1 0x00 Arg2 Arg3 ... ArgN
+ // /\
+ // (inserted)
+ PUSH ebx // Move out Arg3 into Arg4, and also for all K>3 ArgK into ArgK+1
+ mov %edx, %ebx // Move out Arg2 into Arg3
+ xor %edx, %edx // Clear closure 32-bit low register
+
+ // XX: Does this work at all ? This probably breaks the visitors (*and* its unaligned).
+
+ // FIXME: call into the runtime and do a proxy-like-invoke
+ // using a ShadowFrame quick visitor, and then use ArtMethod::Invoke
+ // to call into the actual method (which will take care of fixing up alignment).
+ // Trying to realign in the assembly itself won't actually work
+ // since then the visitor will unwind incorrectly (unless we also fixed up the ManagedStack).
+#endif
+
+ // TODO: avoid extra indirect load by subclass ArtLambdaMethod from ArtMethod.
+
+ // Forward the call to the overwritten receiver method.
+ // -- Arguments [2,N] are left completely untouched since the signature is otherwise identical.
+#if LAMBDA_PROXY_SETUP_FRAME
+ #if LAMBDA_INVOKE_CALLS_INTO_RUNTIME
+ // Have to call into runtime in order to re-align the stack frame to 16 bytes.
+ int3
+ #else
+ // Just call into the method directly. Don't worry about realigning.
+ call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // (new method, new receiver, old args...)
+
+ // The stack frame was manually adjusted, so make sure we have a pop here to fix it back.
+ #if LAMBDA_INVOKE_USES_LONG && !LAMBDA_INVOKE_REALIGN_STACK_FRAME
+
+ POP ecx // OK: ecx is scratch register after the call.
+ // XX: use 'add esp, 4' instead if we need to keep the register? This way we get cleaner CFI.
+ #endif
+ #endif
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+
+#else
+ // Do not use 'call' here since the stack visitors wouldn't know how to visit this frame.
+ jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // tailcall (new method, new receiver, old args...)
+#endif
+
+#if LAMBDA_PROXY_SETUP_FRAME
+ ret
+#endif
+
+END_FUNCTION art_quick_lambda_proxy_invoke_handler
+
/*
* Called to resolve an imt conflict. xmm7 is a hidden argument that holds the target method's
* dex method index.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 17d277e..0a54aa3 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1297,7 +1297,6 @@
RETURN_IF_EAX_ZERO // return or deliver exception
END_FUNCTION art_quick_set64_static
-
DEFINE_FUNCTION art_quick_proxy_invoke_handler
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI
@@ -1309,6 +1308,60 @@
RETURN_OR_DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_proxy_invoke_handler
+// Forward call from boxed innate lambda to the underlying lambda closure's target method.
+DEFINE_FUNCTION art_quick_lambda_proxy_invoke_handler
+ // This function is always called when the lambda is innate.
+ // Therefore we can assume the box is to an innate lambda.
+ // TODO: perhaps there should be a DCHECK to make sure it's innate?
+
+#if LAMBDA_PROXY_SETUP_FRAME
+ // Set up a quick frame when debugging so we can see that it's going through a stub.
+ // Our stack traces will contain the quick lambda proxy hander.
+ // Note that we *must* go through the handler (when spilling) otherwise we won't know how
+ // to move the spilled GC references from the caller to this stub.
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI
+
+ movq %gs:THREAD_SELF_OFFSET, %rdx // Pass Thread::Current().
+ movq %rsp, %rcx // Pass SP.
+ call SYMBOL(artQuickLambdaProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ movq %rax, %xmm0 // Copy return value in case of float returns.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION
+#else
+ // Set up the new correct method receiver (swap object with closure).
+ // -- The original object is no longer available after this.
+ //
+ // (Before)
+ // rsi == mirror::Object* boxed_lambda; // lambda proxy object.
+ movq MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET(%rsi), %rsi
+ // (After)
+ // lambda::Closure* closure = boxed_lambda->closure_; // Overwrite receiver object.
+ // rsi == closure
+
+ // Look up the new correct method target.
+ // -- The original method target is no longer available after this.
+ movq LAMBDA_CLOSURE_METHOD_OFFSET(%rsi), %rdi // Overwrite old receiver method.
+ // (After)
+ // ArtLambdaMethod* lambda_method_target = closure->lambda_info_;
+ // rdi == lambda_method_target
+
+ // TODO: avoid extra indirect load by subclass ArtLambdaMethod from ArtMethod.
+
+ // Set up the correct method target from the lambda info.
+ movq ART_LAMBDA_METHOD_ART_METHOD_OFFSET(%rdi), %rdi // Write new receiver method.
+ // (After)
+ // ArtMethod* method_target = lambda_method_target->target_;
+ // rdi == method_target
+
+ // Forward the call to the overwritten receiver method.
+ // -- Arguments [2,N] are left completely untouched since the signature is otherwise identical.
+ // Do not use 'call' here since the stack would be misaligned (8b instead of 16b).
+ // Also the stack visitors wouldn't know how to visit this frame if we used a call.
+ jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // tailcall (new method, new receiver, old args...)
+#endif
+
+END_FUNCTION art_quick_lambda_proxy_invoke_handler
+
/*
* Called to resolve an imt conflict.
* rax is a hidden argument that holds the target method's dex method index.
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 4166e22..ab42d0e 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -255,7 +255,7 @@
inline const char* ArtField::GetName() SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t field_index = GetDexFieldIndex();
- if (UNLIKELY(GetDeclaringClass()->IsProxyClass())) {
+ if (UNLIKELY(GetDeclaringClass()->IsAnyProxyClass())) {
DCHECK(IsStatic());
DCHECK_LT(field_index, 2U);
return field_index == 0 ? "interfaces" : "throws";
@@ -266,7 +266,7 @@
inline const char* ArtField::GetTypeDescriptor() SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t field_index = GetDexFieldIndex();
- if (UNLIKELY(GetDeclaringClass()->IsProxyClass())) {
+ if (UNLIKELY(GetDeclaringClass()->IsAnyProxyClass())) {
DCHECK(IsStatic());
DCHECK_LT(field_index, 2U);
// 0 == Class[] interfaces; 1 == Class[][] throws;
@@ -290,8 +290,8 @@
inline mirror::Class* ArtField::GetType() {
const uint32_t field_index = GetDexFieldIndex();
auto* declaring_class = GetDeclaringClass();
- if (UNLIKELY(declaring_class->IsProxyClass())) {
- return ProxyFindSystemClass(GetTypeDescriptor());
+ if (UNLIKELY(declaring_class->IsAnyProxyClass())) {
+ return AnyProxyFindSystemClass(GetTypeDescriptor());
}
auto* dex_cache = declaring_class->GetDexCache();
const DexFile* const dex_file = dex_cache->GetDexFile();
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index 3737e0d..3ac563a 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -69,8 +69,8 @@
return nullptr;
}
-mirror::Class* ArtField::ProxyFindSystemClass(const char* descriptor) {
- DCHECK(GetDeclaringClass()->IsProxyClass());
+mirror::Class* ArtField::AnyProxyFindSystemClass(const char* descriptor) {
+ DCHECK(GetDeclaringClass()->IsAnyProxyClass());
return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor);
}
diff --git a/runtime/art_field.h b/runtime/art_field.h
index a943a34..4ebe6fb 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -191,7 +191,9 @@
}
private:
- mirror::Class* ProxyFindSystemClass(const char* descriptor)
+ mirror::Class* AnyProxyFindSystemClass(const char* descriptor)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ mirror::Class* LambdaProxyFindSystemClass(const char* descriptor)
SHARED_REQUIRES(Locks::mutator_lock_);
mirror::Class* ResolveGetType(uint32_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_);
mirror::String* ResolveGetStringName(Thread* self, const DexFile& dex_file, uint32_t string_idx,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index cf548ad..b6e811f 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -292,7 +292,7 @@
}
inline const char* ArtMethod::GetShorty(uint32_t* out_length) {
- DCHECK(!IsProxyMethod());
+ DCHECK(!IsProxyMethod() || IsLambdaProxyMethod()); // OK: lambda proxies use parent dex cache.
const DexFile* dex_file = GetDexFile();
return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length);
}
@@ -354,10 +354,31 @@
}
inline const DexFile::TypeList* ArtMethod::GetParameterTypeList() {
- DCHECK(!IsProxyMethod());
+ // XX: Do proxy methods have a dex file? not sure.
const DexFile* dex_file = GetDexFile();
- const DexFile::ProtoId& proto = dex_file->GetMethodPrototype(
- dex_file->GetMethodId(GetDexMethodIndex()));
+ const DexFile::MethodId* method_id = nullptr;
+
+ if (kIsDebugBuild) {
+ if (UNLIKELY(IsProxyMethod())) {
+ // Proxy method case.
+ CHECK(IsLambdaProxyMethod()) << "Cannot GetParameterTypeList for java.lang.reflect.Proxy";
+
+ //
+ // We do not have a method ID, so look up one of the supers we overrode,
+ // it will have the same exact parameter type list as we do.
+
+ // Lambda proxy classes have the dex cache from their single interface parent.
+ // Proxy classes have multiple interface parents, so they use the root dexcache instead.
+ //
+ // For lambda proxy classes only, get the type list data from the parent.
+ // (code happens to look the same as the usual non-proxy path).
+ }
+ }
+
+ method_id = &dex_file->GetMethodId(GetDexMethodIndex());
+ DCHECK(method_id != nullptr);
+
+ const DexFile::ProtoId& proto = dex_file->GetMethodPrototype(*method_id);
return dex_file->GetProtoParameters(proto);
}
@@ -397,12 +418,20 @@
}
inline mirror::DexCache* ArtMethod::GetDexCache() {
- DCHECK(!IsProxyMethod());
+ DCHECK(!IsProxyMethod() || IsLambdaProxyMethod()); // OK: lambda proxies use parent dex cache.
return GetDeclaringClass()->GetDexCache();
}
inline bool ArtMethod::IsProxyMethod() {
- return GetDeclaringClass()->IsProxyClass();
+ return GetDeclaringClass()->IsAnyProxyClass();
+}
+
+inline bool ArtMethod::IsReflectProxyMethod() {
+ return GetDeclaringClass()->IsReflectProxyClass();
+}
+
+inline bool ArtMethod::IsLambdaProxyMethod() {
+ return GetDeclaringClass()->IsLambdaProxyClass();
}
inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(size_t pointer_size) {
@@ -448,9 +477,9 @@
void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
ArtMethod* interface_method = nullptr;
mirror::Class* klass = declaring_class_.Read();
- if (UNLIKELY(klass != nullptr && klass->IsProxyClass())) {
+ if (UNLIKELY(klass != nullptr && klass->IsAnyProxyClass())) {
// For normal methods, dex cache shortcuts will be visited through the declaring class.
- // However, for proxies we need to keep the interface method alive, so we visit its roots.
+ // However, for any proxies we need to keep the interface method alive, so we visit its roots.
interface_method = mirror::DexCache::GetElementPtrSize(
GetDexCacheResolvedMethods(pointer_size),
GetDexMethodIndex(),
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 5a2d6c3..98f5aee 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -171,8 +171,16 @@
return (GetAccessFlags() & kAccSynthetic) != 0;
}
+ // Does this method live on a declaring class that is itself any proxy class?
+ // -- Returns true for both java.lang.reflect.Proxy and java.lang.LambdaProxy subclasses.
bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+ // Does this method live in a java.lang.reflect.Proxy subclass?
+ bool IsReflectProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Does this method live in a java.lang.LambdaProxy subclass?
+ bool IsLambdaProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+
bool IsPreverified() {
return (GetAccessFlags() & kAccPreverified) != 0;
}
@@ -274,7 +282,15 @@
uint32_t name_and_signature_idx)
SHARED_REQUIRES(Locks::mutator_lock_);
- void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty)
+ // Invoke this method, passing all the virtual registers in args.
+ // -- args_size must be the size in bytes (not size in words)!
+ // -- shorty must be the method shorty (i.e. it includes the return type).
+ // The result is set when the method finishes execution successfully.
+ void Invoke(Thread* self,
+ uint32_t* args,
+ uint32_t args_size, // NOTE: size in bytes
+ /*out*/JValue* result,
+ const char* shorty)
SHARED_REQUIRES(Locks::mutator_lock_);
const void* GetEntryPointFromQuickCompiledCode() {
@@ -428,6 +444,9 @@
mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+ // Returns the current method ('this') if this is a regular, non-proxy method.
+ // Otherwise, when this class is a proxy (IsProxyMethod), look-up the original interface's
+ // method (that the proxy is "overriding") and return that.
ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy(size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index b548dfb..785a9be 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -19,9 +19,12 @@
#if defined(__cplusplus)
#include "art_method.h"
+#include "lambda/art_lambda_method.h"
+#include "lambda/closure.h"
#include "gc/allocator/rosalloc.h"
#include "lock_word.h"
#include "mirror/class.h"
+#include "mirror/lambda_proxy.h"
#include "mirror/string.h"
#include "runtime.h"
#include "thread.h"
@@ -49,6 +52,8 @@
#define ADD_TEST_EQ(x, y) CHECK_EQ(x, y);
#endif
+namespace art {
+
static inline void CheckAsmSupportOffsetsAndSizes() {
#else
#define ADD_TEST_EQ(x, y)
@@ -298,9 +303,80 @@
static_cast<int32_t>(art::gc::allocator::RosAlloc::RunSlotNextOffset()))
// Assert this so that we can avoid zeroing the next field by installing the class pointer.
ADD_TEST_EQ(ROSALLOC_SLOT_NEXT_OFFSET, MIRROR_OBJECT_CLASS_OFFSET)
+// Working with raw lambdas (lambda::Closure) in raw memory:
+//
+// |---------------------|
+// | ArtLambdaMethod* | <-- pointer to lambda art method, has the info like the size.
+// |---------------------| <-- 'data offset'
+// | [ Dynamic Size ] | <-- OPTIONAL: only if the ArtLambdaMethod::dynamic_size_ is true.
+// |---------------------|
+// | Captured Variables |
+// | ... |
+// |---------------------| <-- total length determined by "dynamic size" if it is present,
+// otherwise by the ArtLambdaMethod::static_size_
+
+// Offset from start of lambda::Closure to the ArtLambdaMethod*.
+#define LAMBDA_CLOSURE_METHOD_OFFSET 0
+ADD_TEST_EQ(static_cast<size_t>(LAMBDA_CLOSURE_METHOD_OFFSET),
+ offsetof(art::lambda::ClosureStorage, lambda_info_))
+// Offset from the start of lambda::Closure to the data (captured vars or dynamic size).
+#define LAMBDA_CLOSURE_DATA_OFFSET __SIZEOF_POINTER__
+ADD_TEST_EQ(static_cast<size_t>(LAMBDA_CLOSURE_DATA_OFFSET),
+ offsetof(art::lambda::ClosureStorage, captured_))
+// Offsets to captured variables intentionally omitted as it needs a runtime branch.
+
+// The size of a lambda closure after it's been compressed down for storage.
+// -- Although a lambda closure is a virtual register pair (64-bit), we only need 32-bit
+// to track the pointer when we are on 32-bit architectures.
+// Both the compiler and the runtime therefore compress the closure down for 32-bit archs.
+#define LAMBDA_CLOSURE_COMPRESSED_POINTER_SIZE __SIZEOF_POINTER__
+ADD_TEST_EQ(static_cast<size_t>(LAMBDA_CLOSURE_COMPRESSED_POINTER_SIZE),
+ sizeof(art::lambda::Closure*))
+
+// Working with boxed innate lambdas (as a mirror::Object) in raw memory:
+// --- Note that this layout only applies to lambdas originally made with create-lambda.
+// --- Boxing a lambda created from a new-instance instruction is simply the original object.
+//
+// |---------------------|
+// | object header |
+// |---------------------|
+// | lambda::Closure* | <-- long on 64-bit, int on 32-bit
+// |---------------------|
+#define MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET (MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(static_cast<size_t>(MIRROR_OBJECT_BOXED_INNATE_LAMBDA_CLOSURE_POINTER_OFFSET),
+ art::mirror::LambdaProxy::GetInstanceFieldOffsetClosure().SizeValue())
+ // Equivalent to (private) offsetof(art::mirror::LambdaProxy, closure_))
+
+// Working with boxed innate lambdas (as a mirror::Object) in raw memory:
+// --- Note that this layout only applies to lambdas originally made with create-lambda.
+// --- Boxing a lambda created from a new-instance instruction is simply the original object.
+//
+// |---------------------|
+// | object header |
+// |---------------------|
+// | lambda::Closure* | <-- long on 64-bit, int on 32-bit
+// |---------------------|
+#define ART_LAMBDA_METHOD_ART_METHOD_OFFSET (0)
+ADD_TEST_EQ(static_cast<size_t>(ART_LAMBDA_METHOD_ART_METHOD_OFFSET),
+ art::lambda::ArtLambdaMethod::GetArtMethodOffset())
+
+#if defined(NDEBUG)
+// Release should be faaast. So just jump directly to the lambda method.
+#define LAMBDA_PROXY_SETUP_FRAME 0
+#else
+// Debug can be slower, and we want to get better stack traces. Set up a frame.
+#define LAMBDA_PROXY_SETUP_FRAME 1
+#endif
+
+// For WIP implementation, lambda types are all "longs"
+// which means on a 32-bit implementation we need to fill the argument with 32-bit 0s
+// whenever we invoke a method with a lambda in it.
+// TODO: remove all usages of this once we go to a proper \LambdaType; system.
+#define LAMBDA_INVOKE_USES_LONG 1
#if defined(__cplusplus)
} // End of CheckAsmSupportOffsets.
+} // namespace art
#endif
#endif // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index 969f5b9..e2ade07 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -53,6 +53,7 @@
kAllocatorTagClassTable,
kAllocatorTagInternTable,
kAllocatorTagLambdaBoxTable,
+ kAllocatorTagLambdaProxyClassBoxTable,
kAllocatorTagMaps,
kAllocatorTagLOS,
kAllocatorTagSafeMap,
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 70bd398..6ca56f5 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -65,6 +65,7 @@
Mutex* Locks::trace_lock_ = nullptr;
Mutex* Locks::unexpected_signal_lock_ = nullptr;
Mutex* Locks::lambda_table_lock_ = nullptr;
+Mutex* Locks::lambda_class_table_lock_ = nullptr;
Uninterruptible Roles::uninterruptible_;
struct AllMutexData {
@@ -954,6 +955,7 @@
DCHECK(trace_lock_ != nullptr);
DCHECK(unexpected_signal_lock_ != nullptr);
DCHECK(lambda_table_lock_ != nullptr);
+ DCHECK(lambda_class_table_lock_ != nullptr);
} else {
// Create global locks in level order from highest lock level to lowest.
LockLevel current_lock_level = kInstrumentEntrypointsLock;
@@ -1072,6 +1074,10 @@
DCHECK(lambda_table_lock_ == nullptr);
lambda_table_lock_ = new Mutex("lambda table lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kLambdaClassTableLock);
+ DCHECK(lambda_class_table_lock_ == nullptr);
+ lambda_class_table_lock_ = new Mutex("lambda class table lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index d4c9057..e2d7062 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -60,6 +60,7 @@
kUnexpectedSignalLock,
kThreadSuspendCountLock,
kAbortLock,
+ kLambdaClassTableLock,
kLambdaTableLock,
kJdwpSocketLock,
kRegionSpaceRegionLock,
@@ -692,6 +693,10 @@
// Allow reader-writer mutual exclusion on the boxed table of lambda objects.
// TODO: this should be a RW mutex lock, except that ConditionVariables don't work with it.
static Mutex* lambda_table_lock_ ACQUIRED_AFTER(mutator_lock_);
+
+ // Allow reader-writer mutual exclusion on the boxed table of lambda proxy classes.
+ // TODO: this should be a RW mutex lock, except that ConditionVariables don't work with it.
+ static Mutex* lambda_class_table_lock_ ACQUIRED_AFTER(lambda_table_lock_);
};
class Roles {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2dd2a83..8a0d8d4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -56,6 +56,7 @@
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "lambda/box_class_table.h"
#include "leb128.h"
#include "linear_alloc.h"
#include "mirror/class.h"
@@ -64,6 +65,7 @@
#include "mirror/dex_cache-inl.h"
#include "mirror/field.h"
#include "mirror/iftable-inl.h"
+#include "mirror/lambda_proxy.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
@@ -581,6 +583,9 @@
// Create java.lang.reflect.Proxy root.
SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
+ // Create java.lang.LambdaProxy root.
+ SetClassRoot(kJavaLangLambdaProxy, FindSystemClass(self, "Ljava/lang/LambdaProxy;"));
+
// Create java.lang.reflect.Field.class root.
auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
CHECK(class_root != nullptr);
@@ -1257,6 +1262,7 @@
}
delete data.allocator;
delete data.class_table;
+ delete data.lambda_box_class_table;
}
mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
@@ -1898,8 +1904,10 @@
// Special case to get oat code without overwriting a trampoline.
const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
CHECK(method->IsInvokable()) << PrettyMethod(method);
- if (method->IsProxyMethod()) {
+ if (method->IsReflectProxyMethod()) {
return GetQuickProxyInvokeHandler();
+ } else if (method->IsLambdaProxyMethod()) {
+ return GetQuickLambdaProxyInvokeHandler();
}
bool found;
OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
@@ -3257,7 +3265,7 @@
klass->SetName(soa.Decode<mirror::String*>(name));
klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
- std::string descriptor(GetDescriptorForProxy(klass.Get()));
+ std::string descriptor(GetDescriptorForAnyProxy(klass.Get()));
const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
// Needs to be before we insert the class so that the allocator field is set.
@@ -3377,23 +3385,228 @@
decoded_name->ToModifiedUtf8().c_str()));
CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
- CHECK_EQ(klass.Get()->GetInterfaces(),
+ CHECK_EQ(klass.Get()->GetInterfacesForAnyProxy(),
soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
- CHECK_EQ(klass.Get()->GetThrows(),
+ CHECK_EQ(klass.Get()->GetThrowsForAnyProxy(),
soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
}
return klass.Get();
}
-std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) {
- DCHECK(proxy_class->IsProxyClass());
+mirror::Class* ClassLinker::CreateLambdaProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+ jstring name,
+ jobjectArray interfaces,
+ jobject loader,
+ jobjectArray methods,
+ jobjectArray throws,
+ bool* already_exists) {
+ DCHECK(already_exists != nullptr);
+ *already_exists = false;
+
+ Thread* self = soa.Self();
+ StackHandleScope<10> hs(self);
+
+ // Allocate a new java.lang.Class object for a mirror::Proxy.
+ MutableHandle<mirror::Class> klass =
+ hs.NewHandle(AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)));
+ if (klass.Get() == nullptr) {
+ CHECK(self->IsExceptionPending()); // OOME.
+ return nullptr;
+ }
+ DCHECK(klass->GetClass() != nullptr);
+ klass->SetObjectSize(sizeof(mirror::LambdaProxy));
+
+ // Set the class access flags incl. preverified, so we do not try to set the flag on the methods.
+ klass->SetAccessFlags(kAccClassIsLambdaProxy | kAccPublic | kAccFinal | kAccPreverified);
+ klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader));
+ DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
+ klass->SetName(soa.Decode<mirror::String*>(name));
+ klass->SetDexCache(GetClassRoot(kJavaLangLambdaProxy)->GetDexCache());
+ // Set the status to be just before after loading it, but before anything is resolved.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
+ // Convert "foo.bar.baz" string to "Lfoo/bar/baz;"
+ std::string type_descriptor(GetDescriptorForAnyProxy(klass.Get()));
+
+ mirror::Class* existing;
+ {
+ const size_t hash = ComputeModifiedUtf8Hash(type_descriptor.c_str());
+
+ // Insert the class before loading the fields as the field roots
+ // (ArtField::declaring_class_) are only visited from the class
+ // table. There can't be any suspend points between inserting the
+ // class and setting the field arrays below.
+ existing = InsertClass(type_descriptor.c_str(), klass.Get(), hash);
+ }
+ if (UNLIKELY(existing != nullptr)) {
+ // We had already made the lambda proxy previously. Return it.
+
+ *already_exists = true;
+ return existing;
+ // Let the GC clean up the class we had already allocated but isn't being used.
+ }
+
+ // Needs to be after we insert the class so that the allocator field is set.
+ LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(klass->GetClassLoader());
+
+ // Instance fields are inherited, but we add a couple of static fields...
+ LengthPrefixedArray<ArtField>* sfields =
+ AllocArtFieldArray(self, allocator, mirror::LambdaProxy::kStaticFieldCount);
+ klass->SetSFieldsPtr(sfields);
+
+ // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by
+ // our proxy, so Class.getInterfaces doesn't return the flattened set.
+ // -- private static java.lang.Class[] interfaces; // list of declared interfaces
+ ArtField& interfaces_sfield = sfields->At(mirror::LambdaProxy::kStaticFieldIndexInterfaces);
+ interfaces_sfield.SetDexFieldIndex(mirror::LambdaProxy::kStaticFieldIndexInterfaces);
+ interfaces_sfield.SetDeclaringClass(klass.Get());
+ interfaces_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+
+ // 2. Create a static field 'throws' that holds the classes of exceptions thrown by our methods.
+ // This is returned by java.lang.reflect.Method#getExceptionTypes()
+ // --- private static java.lang.Class[][] throws; // maps vtable id to list of classes.
+ ArtField& throws_sfield = sfields->At(mirror::LambdaProxy::kStaticFieldIndexThrows);
+ throws_sfield.SetDexFieldIndex(mirror::LambdaProxy::kStaticFieldIndexThrows);
+ throws_sfield.SetDeclaringClass(klass.Get());
+ throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+
+ // Set up the Constructor method.
+ {
+ // Lambda proxies have 1 direct method, the constructor.
+ static constexpr size_t kNumDirectMethods = 1;
+ LengthPrefixedArray<ArtMethod>* directs = AllocArtMethodArray(self,
+ allocator,
+ kNumDirectMethods);
+ // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we
+ // want to throw OOM in the future.
+ if (UNLIKELY(directs == nullptr)) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ klass->SetDirectMethodsPtr(directs);
+ CreateLambdaProxyConstructor(klass, klass->GetDirectMethodUnchecked(0, image_pointer_size_));
+ }
+
+ // Create virtual method using specified prototypes.
+ auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods));
+ DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass())
+ << PrettyClass(h_methods->GetClass());
+ const size_t num_virtual_methods = h_methods->GetLength();
+ auto* virtuals = AllocArtMethodArray(self, allocator, num_virtual_methods);
+ // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we
+ // want to throw OOM in the future.
+ if (UNLIKELY(virtuals == nullptr)) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ klass->SetVirtualMethodsPtr(virtuals);
+ size_t abstract_methods = 0;
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ ArtMethod* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
+ ArtMethod* prototype = h_methods->Get(i)->GetArtMethod();
+ if (UNLIKELY((prototype->GetAccessFlags() & kAccDefault) != 0)) {
+ UNIMPLEMENTED(FATAL) << "Lambda proxies don't support default methods yet";
+ }
+ if (prototype->IsAbstract()) {
+ abstract_methods++;
+ }
+ VLOG(class_linker) << "Creating lambda proxy method for " << PrettyMethod(prototype);
+
+ CreateLambdaProxyMethod(klass, prototype, virtual_method);
+ DCHECK(virtual_method->GetDeclaringClass() != nullptr);
+ DCHECK(prototype->GetDeclaringClass() != nullptr);
+ }
+ // Ignore any methods from Object and default methods, it doesn't matter.
+ // Sanity check that the prototype interface is indeed compatible with lambdas.
+ DCHECK_EQ(abstract_methods, 1u)
+ << "Interface must be a single-abstract-method type" << PrettyClass(klass.Get());
+
+ // The super class is java.lang.LambdaProxy
+ klass->SetSuperClass(GetClassRoot(kJavaLangLambdaProxy));
+ // Now effectively in the loaded state.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, self);
+ self->AssertNoPendingException();
+
+ MutableHandle<mirror::Class> new_class = hs.NewHandle<mirror::Class>(nullptr);
+ {
+ // Must hold lock on object when resolved.
+ ObjectLock<mirror::Class> resolution_lock(self, klass);
+ // Link the fields and virtual methods, creating vtable and iftables.
+ // The new class will replace the old one in the class table.
+ Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)));
+
+ {
+ DCHECK_EQ(1, h_interfaces->GetLength()) << "Lambda proxies must implement 1 interface only";
+ mirror::Class* single_abstract_interface = h_interfaces->Get(0);
+ DCHECK(single_abstract_interface != nullptr);
+
+ // Use the dex cache from the interface, which will enable most of the
+ // dex-using mechanisms on the class and its methods will work.
+ klass->SetDexCache(single_abstract_interface->GetDexCache());
+ }
+
+ if (!LinkClass(self, type_descriptor.c_str(), klass, h_interfaces, &new_class)) {
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ return nullptr;
+ }
+ }
+ CHECK(klass->IsRetired());
+ CHECK_NE(klass.Get(), new_class.Get());
+ klass.Assign(new_class.Get());
+
+ CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get());
+ interfaces_sfield.SetObject<false>(klass.Get(),
+ soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+
+ CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get());
+ throws_sfield.SetObject<false>(
+ klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+
+ {
+ // Lock on klass is released. Lock new class object.
+ ObjectLock<mirror::Class> initialization_lock(self, klass);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self);
+ }
+
+ // Sanity checks
+ if (kIsDebugBuild) {
+ CHECK(klass->GetIFieldsPtr() == nullptr);
+ CheckLambdaProxyConstructor(klass->GetDirectMethod(0, image_pointer_size_));
+
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ ArtMethod* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
+ ArtMethod* prototype = h_methods->Get(i++)->GetArtMethod();
+ CheckLambdaProxyMethod(virtual_method, prototype);
+ }
+
+ StackHandleScope<1> hs2(self);
+ Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String*>(name));
+ std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces",
+ decoded_name->ToModifiedUtf8().c_str()));
+ CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name);
+
+ std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws",
+ decoded_name->ToModifiedUtf8().c_str()));
+ CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
+
+ CHECK_EQ(klass.Get()->GetInterfacesForAnyProxy(),
+ soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+ CHECK_EQ(klass.Get()->GetThrowsForAnyProxy(),
+ soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
+ }
+ return klass.Get();
+}
+
+std::string ClassLinker::GetDescriptorForAnyProxy(mirror::Class* proxy_class) {
+ DCHECK(proxy_class != nullptr);
+ DCHECK(proxy_class->IsAnyProxyClass());
mirror::String* name = proxy_class->GetName();
DCHECK(name != nullptr);
return DotToDescriptor(name->ToModifiedUtf8().c_str());
}
ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) {
- DCHECK(proxy_class->IsProxyClass());
+ DCHECK(proxy_class->IsAnyProxyClass());
DCHECK(proxy_method->IsProxyMethod());
{
Thread* const self = Thread::Current();
@@ -3421,7 +3634,7 @@
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
- CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 16u);
+ CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
2, image_pointer_size_);
// Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
@@ -3437,6 +3650,38 @@
out->SetDeclaringClass(klass.Get());
}
+void ClassLinker::CreateLambdaProxyConstructor(Handle<mirror::Class> klass,
+ /*out*/ArtMethod* method_constructor) {
+ DCHECK(klass.Get() != nullptr);
+ DCHECK(method_constructor != nullptr);
+
+ // Create constructor for Proxy that must initialize the method.
+ // Lambda proxy superclass only has 1 direct method, the constructor (<init>()V)
+ CHECK_EQ(GetClassRoot(kJavaLangLambdaProxy)->NumDirectMethods(),
+ mirror::LambdaProxy::kDirectMethodCount);
+ // Get the constructor method.
+ ArtMethod* proxy_constructor = GetClassRoot(kJavaLangLambdaProxy)->GetDirectMethodUnchecked(
+ mirror::LambdaProxy::kDirectMethodIndexConstructor,
+ image_pointer_size_);
+
+ // Verify constructor method is indeed a constructor.
+ CHECK(proxy_constructor != nullptr);
+
+ // Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
+ // constructor method.
+ GetClassRoot(kJavaLangLambdaProxy)->GetDexCache()->SetResolvedMethod(
+ proxy_constructor->GetDexMethodIndex(),
+ proxy_constructor,
+ image_pointer_size_);
+
+ // Clone the existing constructor of LambdaProxy
+ // (our constructor would just invoke it so steal its code_ too).
+ method_constructor->CopyFrom(proxy_constructor, image_pointer_size_);
+ // Make this constructor public and fix the class to be our LambdaProxy version
+ method_constructor->SetAccessFlags((method_constructor->GetAccessFlags() & ~kAccProtected) | kAccPublic);
+ method_constructor->SetDeclaringClass(klass.Get());
+}
+
void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const {
CHECK(constructor->IsConstructor());
auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_);
@@ -3445,6 +3690,14 @@
DCHECK(constructor->IsPublic());
}
+void ClassLinker::CheckLambdaProxyConstructor(ArtMethod* constructor) const {
+ CHECK(constructor->IsConstructor());
+ auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_);
+ CHECK_STREQ(np->GetName(), "<init>");
+ CHECK_STREQ(np->GetSignature().ToString().c_str(), "()V");
+ DCHECK(constructor->IsPublic());
+}
+
void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype,
ArtMethod* out) {
// Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
@@ -3456,6 +3709,7 @@
dex_cache->SetResolvedMethod(
prototype->GetDexMethodIndex(), prototype, image_pointer_size_);
}
+
// We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize
// as necessary
DCHECK(out != nullptr);
@@ -3471,6 +3725,42 @@
out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
}
+void ClassLinker::CreateLambdaProxyMethod(Handle<mirror::Class> klass,
+ ArtMethod* prototype,
+ ArtMethod* out) {
+ DCHECK(prototype != nullptr);
+ DCHECK(out != nullptr);
+
+ // DO NOT go through the proxy invoke handler for the default methods. They have no idea
+ // how to handle the raw closure, so they must get the regular object when invoked.
+ CHECK_EQ(prototype->GetAccessFlags() & kAccDefault, 0u) << "Default methods must not be proxied";
+
+ // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
+ // prototype method
+ auto* dex_cache = prototype->GetDeclaringClass()->GetDexCache();
+ // Avoid dirtying the dex cache unless we need to.
+ if (dex_cache->GetResolvedMethod(prototype->GetDexMethodIndex(), image_pointer_size_) !=
+ prototype) {
+ dex_cache->SetResolvedMethod(
+ prototype->GetDexMethodIndex(), prototype, image_pointer_size_);
+ }
+ // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize
+ // as necessary
+ out->CopyFrom(prototype, image_pointer_size_);
+
+ // Set class to be the concrete proxy class and clear the abstract flag, modify exceptions to
+ // the intersection of throw exceptions as defined in Proxy
+ out->SetDeclaringClass(klass.Get());
+ out->SetAccessFlags((out->GetAccessFlags() & ~kAccAbstract) | kAccFinal);
+
+ // Setting the entry point isn't safe for AOT since ASLR loads it anywhere at runtime.
+ CHECK(!Runtime::Current()->IsAotCompiler());
+
+ // At runtime the method looks like a reference and argument saving method, clone the code
+ // related parameters from this method.
+ out->SetEntryPointFromQuickCompiledCode(GetQuickLambdaProxyInvokeHandler());
+}
+
void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
// Basic sanity
CHECK(!prototype->IsFinal());
@@ -3492,6 +3782,11 @@
prototype->GetReturnType(true /* resolve */, image_pointer_size_));
}
+void ClassLinker::CheckLambdaProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
+ // same as above.
+ return CheckProxyMethod(method, prototype);
+}
+
bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
bool can_init_parents) {
if (can_init_statics && can_init_parents) {
@@ -4123,7 +4418,9 @@
class_loader->SetClassTable(data.class_table);
// Should have been set when we registered the dex file.
data.allocator = class_loader->GetAllocator();
- CHECK(data.allocator != nullptr);
+ CHECK(class_loader->GetLambdaProxyCache() == nullptr);
+ data.lambda_box_class_table = new lambda::BoxClassTable();
+ class_loader->SetLambdaProxyCache(data.lambda_box_class_table);
class_loaders_.push_back(data);
}
return class_table;
@@ -6566,6 +6863,7 @@
"Ljava/lang/reflect/Field;",
"Ljava/lang/reflect/Method;",
"Ljava/lang/reflect/Proxy;",
+ "Ljava/lang/LambdaProxy;",
"[Ljava/lang/String;",
"[Ljava/lang/reflect/Constructor;",
"[Ljava/lang/reflect/Field;",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 29aac31..f073cd8 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -40,6 +40,11 @@
class ImageSpace;
} // namespace space
} // namespace gc
+
+namespace lambda {
+ class BoxClassTable;
+} // namespace lambda
+
namespace mirror {
class ClassLoader;
class DexCache;
@@ -82,6 +87,7 @@
kJavaLangReflectField,
kJavaLangReflectMethod,
kJavaLangReflectProxy,
+ kJavaLangLambdaProxy,
kJavaLangStringArrayClass,
kJavaLangReflectConstructorArrayClass,
kJavaLangReflectFieldArrayClass,
@@ -424,12 +430,46 @@
jobjectArray methods,
jobjectArray throws)
SHARED_REQUIRES(Locks::mutator_lock_);
- std::string GetDescriptorForProxy(mirror::Class* proxy_class)
+
+ // Get the long type descriptor, e.g. "LProxyName$1234;" for the requested proxy class.
+ static std::string GetDescriptorForAnyProxy(mirror::Class* proxy_class)
SHARED_REQUIRES(Locks::mutator_lock_);
ArtMethod* FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method)
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Create a lambda proxy class.
+ // -- Nominally used when boxing an innate lambda, since that has no corresponding class.
+ //
+ // * name must be a fully-qualified class name (and dotted), e.g. "java.lang.Runnable"
+ // * interfaces is an array of java.lang.Class for interfaces that will be the supertype
+ // (note that there must be exactly 1 element here for a lambda interface since lambda
+ // types can only target 1 interface).
+ // * loader must be a java.lang.ClassLoader where the proxy class will be created
+ // * methods must be an array of java.lang.reflect.Method that consists of the
+ // deduplicated methods from all of the interfaces specified.
+ // * throws must be an array of java.lang.Class[] where each index corresponds to that of
+ // methods, and it signifies the "throws" keyword of each method
+ // (this is not directly used by the runtime itself, but it is available via reflection).
+ //
+ // Returns a non-null pointer to a class upon success, otherwise null and throws an exception.
+ //
+ // If the class was already created previously (with the same name but potentially different
+ // parameters), already_exists is set to true; otherwise already_exists is set to false.
+ // The already_exists value is undefined when an exception was thrown.
+ //
+ // Sidenote: interfaces is an array to simplify the libcore code which creates a Java
+ // array in an attempt to reduce code duplication.
+ // TODO: this should probably also take the target single-abstract-method as well.
+ mirror::Class* CreateLambdaProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+ jstring name,
+ jobjectArray interfaces,
+ jobject loader,
+ jobjectArray methods,
+ jobjectArray throws,
+ /*out*/bool* already_exists)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Get the oat code for a method when its class isn't yet initialized
const void* GetQuickOatCodeFor(ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -573,6 +613,7 @@
jweak weak_root; // Weak root to enable class unloading.
ClassTable* class_table;
LinearAlloc* allocator;
+ lambda::BoxClassTable* lambda_box_class_table;
};
// Ensures that the supertype of 'klass' ('supertype') is verified. Returns false and throws
@@ -907,8 +948,12 @@
void CheckProxyConstructor(ArtMethod* constructor) const
SHARED_REQUIRES(Locks::mutator_lock_);
+ void CheckLambdaProxyConstructor(ArtMethod* constructor) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
void CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const
SHARED_REQUIRES(Locks::mutator_lock_);
+ void CheckLambdaProxyMethod(ArtMethod* method, ArtMethod* prototype) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
// For use by ImageWriter to find DexCaches for its roots
ReaderWriterMutex* DexLock()
@@ -926,9 +971,19 @@
void CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out)
SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Copy the constructor from java.lang.LambdaProxy into the 'klass'.
+ // The copy is written into 'method_constructor'.
+ void CreateLambdaProxyConstructor(Handle<mirror::Class> klass,
+ /*out*/ArtMethod* method_constructor)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
void CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out)
SHARED_REQUIRES(Locks::mutator_lock_);
+ void CreateLambdaProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Ensures that methods have the kAccPreverified bit set. We use the kAccPreverfied bit on the
// class access flags to determine whether this has been done before.
void EnsurePreverifiedMethods(Handle<mirror::Class> c)
@@ -940,7 +995,10 @@
// Returns null if not found.
ClassTable* ClassTableForClassLoader(mirror::ClassLoader* class_loader)
SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_);
- // Insert a new class table if not found.
+
+ // Insert a new class table if not found. Uses bootclasspath if class_loader is null.
+ // Returns either the existing table, or the new one if there wasn't one previously
+ // (the return value is always non-null).
ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 2c086c5..4a9db1d 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -31,6 +31,7 @@
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/field.h"
+#include "mirror/lambda_proxy.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
@@ -552,6 +553,7 @@
ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") {
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, allocator_), "allocator");
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, class_table_), "classTable");
+ addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, lambda_proxy_cache_), "lambdaProxyCache");
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, packages_), "packages");
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, parent_), "parent");
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, proxyCache_), "proxyCache");
@@ -564,6 +566,13 @@
};
};
+struct LambdaProxyOffsets : public CheckOffsets<mirror::LambdaProxy> {
+ LambdaProxyOffsets() : CheckOffsets<mirror::LambdaProxy>(false, "Ljava/lang/LambdaProxy;") {
+ addOffset(OFFSETOF_MEMBER(mirror::LambdaProxy, closure_), "closure");
+ };
+};
+
+
struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> {
DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") {
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
@@ -639,6 +648,7 @@
EXPECT_TRUE(StackTraceElementOffsets().Check());
EXPECT_TRUE(ClassLoaderOffsets().Check());
EXPECT_TRUE(ProxyOffsets().Check());
+ EXPECT_TRUE(LambdaProxyOffsets().Check());
EXPECT_TRUE(DexCacheOffsets().Check());
EXPECT_TRUE(ReferenceOffsets().Check());
EXPECT_TRUE(FinalizerReferenceOffsets().Check());
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f705a50..e84313c 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -55,7 +55,6 @@
// Gtests can be very noisy. For example, an executable with multiple tests will trigger native
// bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
// everything else. In case you want to see all messages, comment out the line.
- setenv("ANDROID_LOG_TAGS", "*:e", 1);
art::InitLogging(argv);
LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 87e29ae..2a92226 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -313,7 +313,7 @@
reinterpret_cast<uintptr_t>(virtual_methods)) / method_size;
CHECK_LT(throws_index, static_cast<int>(num_virtuals));
mirror::ObjectArray<mirror::Class>* declared_exceptions =
- proxy_class->GetThrows()->Get(throws_index);
+ proxy_class->GetThrowsForAnyProxy()->Get(throws_index);
mirror::Class* exception_class = exception->GetClass();
bool declares_exception = false;
for (int32_t i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index abf9ac4..8c2dc3e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,9 +23,12 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
+#include "lambda/closure.h"
+#include "lambda/art_lambda_method.h"
#include "method_reference.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
+#include "mirror/lambda_proxy.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
@@ -294,7 +297,8 @@
// 1st GPR.
static mirror::Object* GetProxyThisObject(ArtMethod** sp)
SHARED_REQUIRES(Locks::mutator_lock_) {
- CHECK((*sp)->IsProxyMethod());
+ // TODO: Lambda proxies only set up a frame when debugging
+ CHECK((*sp)->IsReflectProxyMethod() || ((*sp)->IsLambdaProxyMethod() /*&& kIsDebugBuild*/));
CHECK_GT(kNumQuickGprArgs, 0u);
constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR.
size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
@@ -834,8 +838,9 @@
extern "C" uint64_t artQuickProxyInvokeHandler(
ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(proxy_method->IsProxyMethod()) << PrettyMethod(proxy_method);
- DCHECK(receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method);
+ DCHECK(proxy_method->GetDeclaringClass()->IsReflectProxyClass()) << PrettyMethod(proxy_method);
+ DCHECK(proxy_method->IsReflectProxyMethod()) << PrettyMethod(proxy_method);
+ DCHECK(receiver->GetClass()->IsReflectProxyClass()) << PrettyMethod(proxy_method);
// Ensure we don't get thread suspension until the object arguments are safely in jobjects.
const char* old_cause =
self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
@@ -878,6 +883,175 @@
return result.GetJ();
}
+extern "C" uint64_t artQuickLambdaProxyInvokeHandler(
+ ArtMethod* proxy_method, mirror::LambdaProxy* receiver, Thread* self, ArtMethod** sp)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ using lambda::ShortyFieldType;
+
+ DCHECK(proxy_method->GetDeclaringClass()->IsLambdaProxyClass()) << PrettyMethod(proxy_method);
+ DCHECK(proxy_method->IsLambdaProxyMethod()) << PrettyMethod(proxy_method);
+ DCHECK(receiver->GetClass()->IsLambdaProxyClass()) << PrettyMethod(proxy_method);
+
+ lambda::Closure* lambda_closure = receiver->GetClosure();
+ DCHECK(lambda_closure != nullptr); // Should've NPEd during the invoke-interface.
+ // Learned lambdas have their own implementation of the SAM, they must not go through here.
+ DCHECK(lambda_closure->GetLambdaInfo()->IsInnateLambda());
+ ArtMethod* target_method = lambda_closure->GetTargetMethod();
+
+ // Lambda targets are always static.
+ // TODO: This should really be a target_method->IsLambda(), once we add the access flag.
+ CHECK(target_method->IsStatic()) << PrettyMethod(proxy_method) << " "
+ << PrettyMethod(target_method);
+
+ // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
+ const char* old_cause =
+ self->StartAssertNoThreadSuspension("Adding to IRT/SF lambda proxy object arguments");
+ // Register the top of the managed stack, making stack crawlable.
+ DCHECK_EQ((*sp), proxy_method) << PrettyMethod(proxy_method);
+ self->VerifyStack();
+ // Start new JNI local reference state.
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedObjectAccessUnchecked soa(env);
+
+ // Placing arguments into args vector and remove the receiver.
+ ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy(sizeof(void*));
+ CHECK(!non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " "
+ << PrettyMethod(non_proxy_method);
+ uint32_t shorty_len = 0;
+ const char* shorty = non_proxy_method->GetShorty(/*out*/&shorty_len);
+
+ std::vector<jvalue> args;
+ // Make a quick visitor so we can restore the refs incase they move after a GC.
+ BuildQuickArgumentVisitor local_ref_visitor(sp,
+ false /*is_static*/,
+ shorty,
+ shorty_len,
+ &soa,
+ /*out*/&args);
+ local_ref_visitor.VisitArguments();
+
+ static_assert(lambda::kClosureIsStoredAsLong,
+ "Need to update this code once closures are no "
+ "longer treated as a 'long' in quick abi");
+
+ // Allocate one vreg more than usual because we need to convert our
+ // receiver Object (1 vreg) into a long (2 vregs).
+ // TODO: Ugly... move to traits instead?
+ const uint32_t first_arg_reg = ShortyFieldType(ShortyFieldType::kLambda).GetVirtualRegisterCount()
+ - ShortyFieldType(ShortyFieldType::kObject).GetVirtualRegisterCount();
+ const uint32_t num_vregs = lambda_closure->GetLambdaInfo()->GetArgumentVRegCount();
+ DCHECK_GE(num_vregs, first_arg_reg);
+ if (kIsDebugBuild) {
+ const char* method_shorty = non_proxy_method->GetShorty();
+ DCHECK_NE(*method_shorty, '\0') << method_shorty;
+ const char* arg_shorty = method_shorty + 1; // Skip return type.
+
+ // Proxy method should have an object (1 vreg) receiver,
+ // Lambda method should have a lambda (2 vregs) receiver.
+ // -- All other args are the same as before.
+ // -- Make sure vreg count is what we thought it was.
+ uint32_t non_proxy_num_vregs =
+ ShortyFieldType::CountVirtualRegistersRequired(arg_shorty) // doesn't count receiver
+ + ShortyFieldType(ShortyFieldType::kObject).GetVirtualRegisterCount(); // implicit receiver
+
+ CHECK_EQ(non_proxy_num_vregs + first_arg_reg, num_vregs)
+ << PrettyMethod(non_proxy_method) << " " << PrettyMethod(lambda_closure->GetTargetMethod());
+ }
+
+ ShadowFrameAllocaUniquePtr shadow_frame = CREATE_SHADOW_FRAME(num_vregs,
+ /*link*/nullptr,
+ target_method,
+ /*dex_pc*/0);
+
+ // Copy our proxy method caller's arguments into this ShadowFrame.
+ BuildQuickShadowFrameVisitor local_sf_visitor(sp,
+ /*is_static*/false,
+ shorty,
+ shorty_len,
+ shadow_frame.get(),
+ first_arg_reg);
+
+ local_sf_visitor.VisitArguments();
+ // Now fix up the arguments, with each ArgK being a vreg:
+
+ // (Before):
+ // Arg0 = proxy receiver (LambdaProxy)
+ // Arg1 = first-user defined argument
+ // Arg2 = second user-defined argument
+ // ....
+ // ArgN = ...
+
+ // (After)
+ // Arg0 = closure (hi)
+ // Arg1 = closure (lo) = 0x00 on 32-bit
+ // Arg2 = <?> (first user-defined argument)
+ // Arg3 = <?> (first user-defined argument)
+ // ...
+ // argN+1 = ...
+
+ // Transformation diagram:
+ /*
+ Arg0 Arg2 Arg3 ... ArgN
+ | \ \ \
+ | \ \ \
+ ClHi ClLo Arg2 Arg3 ... ArgN:
+ */
+
+ // 1) memmove vregs 1-N into 2-N+1
+ uint32_t* shadow_frame_vregs = shadow_frame->GetVRegArgs(/*i*/0);
+ if (lambda::kClosureIsStoredAsLong ||
+ sizeof(void*) != sizeof(mirror::CompressedReference<mirror::LambdaProxy>)) {
+ // Suspending here would be very bad since we are doing a raw memmove
+
+ // Move the primitive vregs over.
+ {
+ size_t shadow_frame_vregs_size = num_vregs;
+ memmove(shadow_frame_vregs + first_arg_reg,
+ shadow_frame_vregs,
+ shadow_frame_vregs_size - first_arg_reg);
+ }
+
+ // Move the reference vregs over.
+ if (LIKELY(shadow_frame->HasReferenceArray())) {
+ uint32_t* shadow_frame_references = shadow_frame_vregs + num_vregs;
+ size_t shadow_frame_references_size = num_vregs;
+ memmove(shadow_frame_references + first_arg_reg,
+ shadow_frame_references,
+ shadow_frame_references_size - first_arg_reg);
+ }
+
+ static_assert(lambda::kClosureSupportsReadBarrier == false,
+ "Using this memmove code with a read barrier GC seems like it could be unsafe.");
+
+ static_assert(sizeof(mirror::CompressedReference<mirror::LambdaProxy>) == sizeof(uint32_t),
+ "This block of code assumes a compressed reference fits into exactly 1 vreg");
+ }
+ // 2) replace proxy receiver with lambda
+ shadow_frame->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<uintptr_t>(lambda_closure)));
+
+ // OK: After we do the invoke, the target method takes over managing the arguments
+ // and we won't ever access the shadow frame again (if any references moved).
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ // The shadow frame vreg contents are now 'owned' by the Invoke method, and
+ // will be managed by it during a GC despite being a raw uint32_t array.
+ // We however have no guarantee that it is updated on the way out, so do not read out of the
+ // shadow frame after this call.
+ JValue result;
+ target_method->Invoke(self,
+ shadow_frame_vregs,
+ num_vregs * sizeof(uint32_t),
+ /*out*/&result,
+ target_method->GetShorty());
+
+ // Restore references on the proxy caller stack frame which might have moved.
+ // -- This is necessary because the QuickFrameInfo is just the generic runtime "RefsAndArgs"
+ // which means that the regular stack visitor wouldn't know how to GC-move any references
+ // that we spilled ourselves in the proxy stub.
+ local_ref_visitor.FixupReferences();
+ return result.GetJ();
+}
+
// Read object references held in arguments from quick frames and place in a JNI local references,
// so they don't get garbage collected.
class RememberForGcArgumentVisitor FINAL : public QuickArgumentVisitor {
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index 2842c5a..1ef7585 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -17,6 +17,10 @@
#ifndef ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
#define ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_
+// Define entry points to assembly routines.
+// All extern "C" functions here are defined in a corresponding assembly-only file.
+// The exact file paths are runtime/arch/$ISA/quick_entrypoints_$ISA.s
+
namespace art {
#ifndef BUILDING_LIBART
@@ -52,6 +56,13 @@
return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
}
+// Return the address of quick stub code for handling transitions into the lambda proxy
+// invoke handler.
+extern "C" void art_quick_lambda_proxy_invoke_handler();
+static inline const void* GetQuickLambdaProxyInvokeHandler() {
+ return reinterpret_cast<const void*>(art_quick_lambda_proxy_invoke_handler);
+}
+
// Return the address of quick stub code for resolving a method at first call.
extern "C" void art_quick_resolution_trampoline(ArtMethod*);
static inline const void* GetQuickResolutionStub() {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index da9a79e..07f0628 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -672,8 +672,8 @@
return result;
} else if (UNLIKELY(klass->IsPrimitive<kVerifyNone>())) {
return Primitive::Descriptor(klass->GetPrimitiveType<kVerifyNone>());
- } else if (UNLIKELY(klass->IsProxyClass<kVerifyNone>())) {
- return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(klass);
+ } else if (UNLIKELY(klass->IsAnyProxyClass<kVerifyNone>())) {
+ return Runtime::Current()->GetClassLinker()->GetDescriptorForAnyProxy(klass);
} else {
mirror::DexCache* dex_cache = klass->GetDexCache<kVerifyNone>();
if (!IsValidContinuousSpaceObjectAddress(dex_cache)) {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 9f6699f..2de8e7e 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -888,12 +888,56 @@
return false;
}
+ StackHandleScope<1> hs{self}; // NOLINT: [readability/braces] [4];
+
+ // Use the lambda method's class loader since it's close enough.
+ // TODO: create-lambda should capture the current method's class loader and use that instead.
+ // TODO: Do we want create-lambda to work for static methods outside of the declaring class?
+ // --> then we need to store a classloader in the lambda method. otherwise we don't
+ // because it would always use the declaring class's class loader.
+ // TODO: add a GetClassLoader to the lambda closure which knows how to do this,
+ // don't hardcode this here.
+ Handle<ClassLoader> current_class_loader = hs.NewHandle(
+ lambda_closure->GetTargetMethod()->GetDeclaringClass()->GetClassLoader());
+
+ // TODO: get the type ID from the instruction
+ std::string class_name;
+ {
+ // Temporary hack to read the interface corresponding to a box-lambda.
+ // TODO: The box-lambda should encode the type ID instead, so we don't need to do this.
+ {
+ // Do a hack where we read from const-string the interface name
+ mirror::Object* string_reference = shadow_frame.GetVRegReference(vreg_target_object);
+
+ CHECK(string_reference != nullptr)
+ << "box-lambda needs the type name stored in string vA (target), but it was null";
+
+ CHECK(string_reference->IsString())
+ << "box-lambda needs the type name stored in string vA (target)";
+
+ mirror::String* as_string = string_reference->AsString();
+ class_name = as_string->ToModifiedUtf8();
+ }
+
+ // Trigger class loading of the functional interface.
+ // TODO: This should actually be done by the create-lambda...
+ if (Runtime::Current()->GetClassLinker()
+ ->FindClass(self, class_name.c_str(), current_class_loader) == nullptr) {
+ CHECK(self->IsExceptionPending());
+ self->AssertPendingException();
+ return false;
+ }
+ }
+
mirror::Object* closure_as_object =
- Runtime::Current()->GetLambdaBoxTable()->BoxLambda(lambda_closure);
+ Runtime::Current()->GetLambdaBoxTable()->BoxLambda(lambda_closure,
+ class_name.c_str(),
+ current_class_loader.Get());
// Failed to box the lambda, an exception was raised.
if (UNLIKELY(closure_as_object == nullptr)) {
CHECK(self->IsExceptionPending());
+ shadow_frame.SetVRegReference(vreg_target_object, nullptr);
return false;
}
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index bf95a0e..11a8c2e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -102,6 +102,8 @@
size_t lambda_captured_variable_index = 0;
while (true) {
dex_pc = inst->GetDexPc(insns);
+ DCHECK_LE(dex_pc, code_item->insns_size_in_code_units_)
+ << "Dex PC overflowed code item size; missing return instruction?";
shadow_frame.SetDexPC(dex_pc);
TraceExecution(shadow_frame, inst, dex_pc);
inst_data = inst->Fetch16(0);
diff --git a/runtime/lambda/art_lambda_method.cc b/runtime/lambda/art_lambda_method.cc
index 6f9f8bb..0690cd1 100644
--- a/runtime/lambda/art_lambda_method.cc
+++ b/runtime/lambda/art_lambda_method.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
#include "lambda/art_lambda_method.h"
#include "base/logging.h"
@@ -73,5 +74,12 @@
}
}
+size_t ArtLambdaMethod::GetArgumentVRegCount() const {
+ DCHECK(GetArtMethod()->IsStatic()); // Instance methods don't have receiver in shorty.
+ const char* method_shorty = GetArtMethod()->GetShorty();
+ DCHECK_NE(*method_shorty, '\0') << method_shorty;
+ return ShortyFieldType::CountVirtualRegistersRequired(method_shorty + 1); // skip return type
+}
+
} // namespace lambda
} // namespace art
diff --git a/runtime/lambda/art_lambda_method.h b/runtime/lambda/art_lambda_method.h
index ea13eb7..a858bf9 100644
--- a/runtime/lambda/art_lambda_method.h
+++ b/runtime/lambda/art_lambda_method.h
@@ -90,6 +90,17 @@
return strlen(captured_variables_shorty_);
}
+ // Return the offset in bytes from the start of ArtLambdaMethod to the method_.
+ // -- Only should be used by assembly (stubs) support code and compiled code.
+ static constexpr size_t GetArtMethodOffset() {
+ return offsetof(ArtLambdaMethod, method_);
+ }
+
+ // Calculate how many vregs all the arguments will use when doing an invoke.
+ // (Most primitives are 1 vregs, double/long are 2, reference is 1, lambda is 2).
+ // -- This is used to know how big to set up shadow frame when invoking into the target method.
+ size_t GetArgumentVRegCount() const SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
// TODO: ArtMethod, or at least the entry points should be inlined into this struct
// to avoid an extra indirect load when doing invokes.
diff --git a/runtime/lambda/box_class_table-inl.h b/runtime/lambda/box_class_table-inl.h
new file mode 100644
index 0000000..2fc34a7
--- /dev/null
+++ b/runtime/lambda/box_class_table-inl.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_INL_H_
+#define ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_INL_H_
+
+#include "lambda/box_class_table.h"
+#include "thread.h"
+
+namespace art {
+namespace lambda {
+
+template <typename Visitor>
+inline void BoxClassTable::VisitRoots(const Visitor& visitor) {
+ MutexLock mu(Thread::Current(), *Locks::lambda_class_table_lock_);
+ for (std::pair<UnorderedMapKeyType, ValueType>& key_value : map_) {
+ ValueType& gc_root = key_value.second;
+ visitor.VisitRoot(gc_root.AddressWithoutBarrier());
+ }
+}
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_INL_H_
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
diff --git a/runtime/lambda/box_class_table.h b/runtime/lambda/box_class_table.h
new file mode 100644
index 0000000..17e1026
--- /dev/null
+++ b/runtime/lambda/box_class_table.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+#ifndef ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_H_
+#define ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_H_
+
+#include "base/allocator.h"
+#include "base/hash_map.h"
+#include "gc_root.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "object_callbacks.h"
+
+#include <stdint.h>
+
+namespace art {
+
+class ArtMethod; // forward declaration
+template<class T> class Handle; // forward declaration
+
+namespace mirror {
+class Class; // forward declaration
+class ClassLoader; // forward declaration
+class LambdaProxy; // forward declaration
+class Object; // forward declaration
+} // namespace mirror
+
+namespace lambda {
+struct Closure; // forward declaration
+
+/*
+ * Store a table of boxed lambdas. This is required to maintain object referential equality
+ * when a lambda is re-boxed.
+ *
+ * Conceptually, we store a mapping of Class Name -> Weak Reference<Class>.
+ * When too many objects get GCd, we shrink the underlying table to use less space.
+ */
+class BoxClassTable FINAL {
+ public:
+ // TODO: This should take a LambdaArtMethod instead, read class name from that.
+ // Note: null class_loader means bootclasspath.
+ mirror::Class* GetOrCreateBoxClass(const char* class_name,
+ const Handle<mirror::ClassLoader>& class_loader)
+ REQUIRES(!Locks::lambda_class_table_lock_, !Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Sweep strong references to lambda class boxes. Update the addresses if the objects
+ // have been moved, and delete them from the table if the objects have been cleaned up.
+ template <typename Visitor>
+ void VisitRoots(const Visitor& visitor)
+ NO_THREAD_SAFETY_ANALYSIS // for object marking requiring heap bitmap lock
+ REQUIRES(!Locks::lambda_class_table_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ BoxClassTable();
+ ~BoxClassTable();
+
+ private:
+ // We only store strong GC roots in our table.
+ using ValueType = GcRoot<mirror::Class>;
+
+ // Attempt to look up the class in the map, or return null if it's not there yet.
+ ValueType FindBoxedClass(const std::string& class_name) const
+ SHARED_REQUIRES(Locks::lambda_class_table_lock_);
+
+ // Store the key as a string so that we can have our own copy of the class name.
+ using UnorderedMapKeyType = std::string;
+
+ // EmptyFn implementation for art::HashMap
+ struct EmptyFn {
+ void MakeEmpty(std::pair<UnorderedMapKeyType, ValueType>& item) const
+ NO_THREAD_SAFETY_ANALYSIS;
+ // SHARED_REQUIRES(Locks::mutator_lock_);
+
+ bool IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const;
+ };
+
+ // HashFn implementation for art::HashMap
+ struct HashFn {
+ size_t operator()(const UnorderedMapKeyType& key) const
+ NO_THREAD_SAFETY_ANALYSIS;
+ // SHARED_REQUIRES(Locks::mutator_lock_);
+ };
+
+ // EqualsFn implementation for art::HashMap
+ struct EqualsFn {
+ bool operator()(const UnorderedMapKeyType& lhs, const UnorderedMapKeyType& rhs) const
+ NO_THREAD_SAFETY_ANALYSIS;
+ // SHARED_REQUIRES(Locks::mutator_lock_);
+ };
+
+ using UnorderedMap = art::HashMap<UnorderedMapKeyType,
+ ValueType,
+ EmptyFn,
+ HashFn,
+ EqualsFn,
+ TrackingAllocator<std::pair<UnorderedMapKeyType, ValueType>,
+ kAllocatorTagLambdaProxyClassBoxTable>>;
+
+ // Map of strong GC roots (lambda interface name -> lambda proxy class)
+ UnorderedMap map_ GUARDED_BY(Locks::lambda_class_table_lock_);
+
+ // Shrink the map when we get below this load factor.
+ // (This is an arbitrary value that should be large enough to prevent aggressive map erases
+ // from shrinking the table too often.)
+ static constexpr double kMinimumLoadFactor = UnorderedMap::kDefaultMinLoadFactor / 2;
+
+ DISALLOW_COPY_AND_ASSIGN(BoxClassTable);
+};
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_BOX_CLASS_TABLE_H_
diff --git a/runtime/lambda/box_table.cc b/runtime/lambda/box_table.cc
index 9918bb7..0032d08 100644
--- a/runtime/lambda/box_table.cc
+++ b/runtime/lambda/box_table.cc
@@ -18,8 +18,10 @@
#include "base/mutex.h"
#include "common_throws.h"
#include "gc_root-inl.h"
+#include "lambda/box_class_table.h"
#include "lambda/closure.h"
#include "lambda/leaking_allocator.h"
+#include "mirror/lambda_proxy.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "thread.h"
@@ -28,12 +30,13 @@
namespace art {
namespace lambda {
-// Temporarily represent the lambda Closure as its raw bytes in an array.
-// TODO: Generate a proxy class for the closure when boxing the first time.
-using BoxedClosurePointerType = mirror::ByteArray*;
+// All closures are boxed into a subtype of LambdaProxy which implements the lambda's interface.
+using BoxedClosurePointerType = mirror::LambdaProxy*;
-static mirror::Class* GetBoxedClosureClass() SHARED_REQUIRES(Locks::mutator_lock_) {
- return mirror::ByteArray::GetArrayClass();
+// Returns the base class for all boxed closures.
+// Note that concrete closure boxes are actually a subtype of mirror::LambdaProxy.
+static mirror::Class* GetBoxedClosureBaseClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangLambdaProxy);
}
namespace {
@@ -54,6 +57,14 @@
return closure;
}
};
+
+ struct DeleterForClosure {
+ void operator()(Closure* closure) const {
+ ClosureAllocator::Delete(closure);
+ }
+ };
+
+ using UniqueClosurePtr = std::unique_ptr<Closure, DeleterForClosure>;
} // namespace
BoxTable::BoxTable()
@@ -75,7 +86,9 @@
}
}
-mirror::Object* BoxTable::BoxLambda(const ClosureType& closure) {
+mirror::Object* BoxTable::BoxLambda(const ClosureType& closure,
+ const char* class_name,
+ mirror::ClassLoader* class_loader) {
Thread* self = Thread::Current();
{
@@ -91,7 +104,7 @@
// Functional f = () -> 5; // vF = create-lambda
// Object a = f; // vA = box-lambda vA
// Object b = f; // vB = box-lambda vB
- // assert(a == f)
+ // assert(a == b)
ValueType value = FindBoxedLambda(closure);
if (!value.IsNull()) {
return value.Read();
@@ -100,30 +113,62 @@
// Otherwise we need to box ourselves and insert it into the hash map
}
- // Release the lambda table lock here, so that thread suspension is allowed.
+ // Convert the Closure into a managed object instance, whose supertype of java.lang.LambdaProxy.
- // Convert the Closure into a managed byte[] 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.
- // It's also good enough to contain all the captured primitive variables.
-
- // 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
- BoxedClosurePointerType closure_as_array_object =
- mirror::ByteArray::Alloc(self, closure->GetSize());
+ StackHandleScope<2> hs{self}; // NOLINT: [readability/braces] [4]
- // There are no thread suspension points after this, so we don't need to put it into a handle.
+ Handle<mirror::ClassLoader> class_loader_handle = hs.NewHandle(class_loader);
- if (UNLIKELY(closure_as_array_object == nullptr)) {
+ // Release the lambda table lock here, so that thread suspension is allowed.
+ self->AllowThreadSuspension();
+
+ lambda::BoxClassTable* lambda_box_class_table;
+
+ // Find the lambda box class table, which can be in the system class loader if classloader is null
+ if (class_loader == nullptr) {
+ ScopedObjectAccessUnchecked soa(self);
+ mirror::ClassLoader* system_class_loader =
+ soa.Decode<mirror::ClassLoader*>(Runtime::Current()->GetSystemClassLoader());
+ lambda_box_class_table = system_class_loader->GetLambdaProxyCache();
+ } else {
+ lambda_box_class_table = class_loader_handle->GetLambdaProxyCache();
+ // OK: can't be deleted while we hold a handle to the class loader.
+ }
+ DCHECK(lambda_box_class_table != nullptr);
+
+ Handle<mirror::Class> closure_class(hs.NewHandle(
+ lambda_box_class_table->GetOrCreateBoxClass(class_name, class_loader_handle)));
+ if (UNLIKELY(closure_class.Get() == nullptr)) {
// Most likely an OOM has occurred.
- CHECK(self->IsExceptionPending());
+ self->AssertPendingException();
return nullptr;
}
- // Write the raw closure data into the byte[].
- closure->CopyTo(closure_as_array_object->GetRawData(sizeof(uint8_t), // component size
- 0 /*index*/), // index
- closure_as_array_object->GetLength());
+ BoxedClosurePointerType closure_as_object = nullptr;
+ UniqueClosurePtr closure_table_copy;
+ // Create an instance of the class, and assign the pointer to the closure into it.
+ {
+ closure_as_object = down_cast<BoxedClosurePointerType>(closure_class->AllocObject(self));
+ if (UNLIKELY(closure_as_object == nullptr)) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ // Make a copy of the closure that we will store in the hash map.
+ // The proxy instance will also point to this same hash map.
+ // Note that the closure pointer is cleaned up only after the proxy is GCd.
+ closure_table_copy.reset(ClosureAllocator::Allocate(closure->GetSize()));
+ closure_as_object->SetClosure(closure_table_copy.get());
+ }
+
+ // There are no thread suspension points after this, so we don't need to put it into a handle.
+ ScopedAssertNoThreadSuspension soants{self, // NOLINT: [whitespace/braces] [5]
+ "box lambda table - box lambda - no more suspensions"}; // NOLINT: [whitespace/braces] [5]
+
+ // Write the raw closure data into the proxy instance's copy of the closure.
+ closure->CopyTo(closure_table_copy.get(),
+ closure->GetSize());
// The method has been successfully boxed into an object, now insert it into the hash map.
{
@@ -134,24 +179,21 @@
// we were allocating the object before.
ValueType value = FindBoxedLambda(closure);
if (UNLIKELY(!value.IsNull())) {
- // Let the GC clean up method_as_object at a later time.
+ // Let the GC clean up closure_as_object at a later time.
+ // (We will not see this object when sweeping, it wasn't inserted yet.)
+ closure_as_object->SetClosure(nullptr);
return value.Read();
}
// Otherwise we need to insert it into the hash map in this thread.
- // Make a copy for the box table to keep, in case the closure gets collected from the stack.
- // TODO: GC may need to sweep for roots in the box table's copy of the closure.
- Closure* closure_table_copy = ClosureAllocator::Allocate(closure->GetSize());
- closure->CopyTo(closure_table_copy, closure->GetSize());
-
- // The closure_table_copy needs to be deleted by us manually when we erase it from the map.
+ // The closure_table_copy is deleted by us manually when we erase it from the map.
// Actually insert into the table.
- map_.Insert({closure_table_copy, ValueType(closure_as_array_object)});
+ map_.Insert({closure_table_copy.release(), ValueType(closure_as_object)});
}
- return closure_as_array_object;
+ return closure_as_object;
}
bool BoxTable::UnboxLambda(mirror::Object* object, ClosureType* out_closure) {
@@ -165,29 +207,35 @@
mirror::Object* boxed_closure_object = object;
- // Raise ClassCastException if object is not instanceof byte[]
- if (UNLIKELY(!boxed_closure_object->InstanceOf(GetBoxedClosureClass()))) {
- ThrowClassCastException(GetBoxedClosureClass(), boxed_closure_object->GetClass());
+ // Raise ClassCastException if object is not instanceof LambdaProxy
+ if (UNLIKELY(!boxed_closure_object->InstanceOf(GetBoxedClosureBaseClass()))) {
+ ThrowClassCastException(GetBoxedClosureBaseClass(), boxed_closure_object->GetClass());
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 byte[].
+ // specified in [type id]. This is not currently implemented since the type id is unavailable.
// If we got this far, the inputs are valid.
- // Shuffle the byte[] back into a raw closure, then allocate it, copy, and return it.
- BoxedClosurePointerType boxed_closure_as_array =
+ // Shuffle the java.lang.LambdaProxy back into a raw closure, then allocate it, copy,
+ // and return it.
+ BoxedClosurePointerType boxed_closure =
down_cast<BoxedClosurePointerType>(boxed_closure_object);
- const int8_t* unaligned_interior_closure = boxed_closure_as_array->GetData();
+ DCHECK_ALIGNED(boxed_closure->GetClosure(), alignof(Closure));
+ const Closure* aligned_interior_closure = boxed_closure->GetClosure();
+ DCHECK(aligned_interior_closure != nullptr);
+
+ // TODO: we probably don't need to make a copy here later on, once there's GC support.
// Allocate a copy that can "escape" and copy the closure data into that.
Closure* unboxed_closure =
- LeakingAllocator::MakeFlexibleInstance<Closure>(self, boxed_closure_as_array->GetLength());
+ LeakingAllocator::MakeFlexibleInstance<Closure>(self, aligned_interior_closure->GetSize());
+ DCHECK_ALIGNED(unboxed_closure, alignof(Closure));
// TODO: don't just memcpy the closure, it's unsafe when we add references to the mix.
- memcpy(unboxed_closure, unaligned_interior_closure, boxed_closure_as_array->GetLength());
+ memcpy(unboxed_closure, aligned_interior_closure, aligned_interior_closure->GetSize());
- DCHECK_EQ(unboxed_closure->GetSize(), static_cast<size_t>(boxed_closure_as_array->GetLength()));
+ DCHECK_EQ(unboxed_closure->GetSize(), aligned_interior_closure->GetSize());
*out_closure = unboxed_closure;
return true;
@@ -236,9 +284,10 @@
if (new_value == nullptr) {
// The object has been swept away.
- const ClosureType& closure = key_value_pair.first;
+ Closure* closure = key_value_pair.first;
// Delete the entry from the map.
+ // (Remove from map first to avoid accessing dangling pointer).
map_iterator = map_.Erase(map_iterator);
// Clean up the memory by deleting the closure.
@@ -290,7 +339,10 @@
}
bool BoxTable::EmptyFn::IsEmpty(const std::pair<UnorderedMapKeyType, ValueType>& item) const {
- return item.first == nullptr;
+ bool is_empty = item.first == nullptr;
+ DCHECK_EQ(item.second.IsNull(), is_empty);
+
+ return is_empty;
}
bool BoxTable::EqualsFn::operator()(const UnorderedMapKeyType& lhs,
diff --git a/runtime/lambda/box_table.h b/runtime/lambda/box_table.h
index adb7332..9dca6ab 100644
--- a/runtime/lambda/box_table.h
+++ b/runtime/lambda/box_table.h
@@ -30,6 +30,9 @@
class ArtMethod; // forward declaration
namespace mirror {
+class Class; // forward declaration
+class ClassLoader; // forward declaration
+class LambdaProxy; // forward declaration
class Object; // forward declaration
} // namespace mirror
@@ -48,8 +51,11 @@
using ClosureType = art::lambda::Closure*;
// Boxes a closure into an object. Returns null and throws an exception on failure.
- mirror::Object* BoxLambda(const ClosureType& closure)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::lambda_table_lock_);
+ mirror::Object* BoxLambda(const ClosureType& closure,
+ const char* class_name,
+ mirror::ClassLoader* class_loader)
+ REQUIRES(!Locks::lambda_table_lock_, !Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Unboxes an object back into the lambda. Returns false and throws an exception on failure.
bool UnboxLambda(mirror::Object* object, ClosureType* out_closure)
@@ -128,7 +134,16 @@
TrackingAllocator<std::pair<ClosureType, ValueType>,
kAllocatorTagLambdaBoxTable>>;
+ using ClassMap = art::HashMap<std::string,
+ GcRoot<mirror::Class>,
+ EmptyFn,
+ HashFn,
+ EqualsFn,
+ TrackingAllocator<std::pair<ClosureType, ValueType>,
+ kAllocatorTagLambdaProxyClassBoxTable>>;
+
UnorderedMap map_ GUARDED_BY(Locks::lambda_table_lock_);
+ UnorderedMap classes_map_ GUARDED_BY(Locks::lambda_table_lock_);
bool allow_new_weaks_ GUARDED_BY(Locks::lambda_table_lock_);
ConditionVariable new_weaks_condition_ GUARDED_BY(Locks::lambda_table_lock_);
diff --git a/runtime/lambda/closure.cc b/runtime/lambda/closure.cc
index 179e4ee..f935e04 100644
--- a/runtime/lambda/closure.cc
+++ b/runtime/lambda/closure.cc
@@ -20,9 +20,6 @@
#include "lambda/art_lambda_method.h"
#include "runtime/mirror/object_reference.h"
-static constexpr const bool kClosureSupportsReferences = false;
-static constexpr const bool kClosureSupportsGarbageCollection = false;
-
namespace art {
namespace lambda {
@@ -128,6 +125,10 @@
return const_cast<ArtMethod*>(lambda_info_->GetArtMethod());
}
+ArtLambdaMethod* Closure::GetLambdaInfo() const {
+ return const_cast<ArtLambdaMethod*>(lambda_info_);
+}
+
uint32_t Closure::GetHashCode() const {
// Start with a non-zero constant, a prime number.
uint32_t result = 17;
diff --git a/runtime/lambda/closure.h b/runtime/lambda/closure.h
index 31ff194..38ec063 100644
--- a/runtime/lambda/closure.h
+++ b/runtime/lambda/closure.h
@@ -33,12 +33,52 @@
class ArtLambdaMethod; // forward declaration
class ClosureBuilder; // forward declaration
+// TODO: Remove these constants once closures are supported properly.
+
+// Does the lambda closure support containing references? If so, all the users of lambdas
+// must be updated to also support references.
+static constexpr const bool kClosureSupportsReferences = false;
+// Does the lambda closure support being garbage collected? If so, all the users of lambdas
+// must be updated to also support garbage collection.
+static constexpr const bool kClosureSupportsGarbageCollection = false;
+// Does the lambda closure support being garbage collected with a read barrier? If so,
+// all the users of the lambdas msut also be updated to support read barrier GC.
+static constexpr const bool kClosureSupportsReadBarrier = false;
+
+// Is this closure being stored as a 'long' in shadow frames and the quick ABI?
+static constexpr const bool kClosureIsStoredAsLong = true;
+
+
+// Raw memory layout for the lambda closure.
+//
+// WARNING:
+// * This should only be used by the compiler and tests, as they need to offsetof the raw fields.
+// * Runtime/interpreter should always access closures through a Closure pointer.
+struct ClosureStorage {
+ // Compile-time known lambda information such as the type descriptor and size.
+ ArtLambdaMethod* lambda_info_;
+
+ // A contiguous list of captured variables, and possibly the closure size.
+ // The runtime size can always be determined through GetSize().
+ union {
+ // Read from here if the closure size is static (ArtLambdaMethod::IsStatic)
+ uint8_t static_variables_[0];
+ struct {
+ // Read from here if the closure size is dynamic (ArtLambdaMethod::IsDynamic)
+ size_t size_; // The lambda_info_ and the size_ itself is also included as part of the size.
+ uint8_t variables_[0];
+ } dynamic_;
+ } captured_[0];
+ // captured_ will always consist of one array element at runtime.
+ // Set to [0] so that 'size_' is not counted in sizeof(Closure).
+};
+
// Inline representation of a lambda closure.
// Contains the target method and the set of packed captured variables as a copy.
//
// The closure itself is logically immutable, although in practice any object references
// it (recursively) contains can be moved and updated by the GC.
-struct PACKED(sizeof(ArtLambdaMethod*)) Closure {
+struct Closure : private ClosureStorage {
// Get the size of the Closure in bytes.
// This is necessary in order to allocate a large enough area to copy the Closure into.
// Do *not* copy the closure with memcpy, since references also need to get moved.
@@ -52,6 +92,9 @@
// Get the target method, i.e. the method that will be dispatched into with invoke-lambda.
ArtMethod* GetTargetMethod() const;
+ // Get the static lambda info that never changes.
+ ArtLambdaMethod* GetLambdaInfo() const;
+
// Calculates the hash code. Value is recomputed each time.
uint32_t GetHashCode() const SHARED_REQUIRES(Locks::mutator_lock_);
@@ -156,28 +199,15 @@
static size_t GetClosureSize(const uint8_t* closure);
///////////////////////////////////////////////////////////////////////////////////
-
- // Compile-time known lambda information such as the type descriptor and size.
- ArtLambdaMethod* lambda_info_;
-
- // A contiguous list of captured variables, and possibly the closure size.
- // The runtime size can always be determined through GetSize().
- union {
- // Read from here if the closure size is static (ArtLambdaMethod::IsStatic)
- uint8_t static_variables_[0];
- struct {
- // Read from here if the closure size is dynamic (ArtLambdaMethod::IsDynamic)
- size_t size_; // The lambda_info_ and the size_ itself is also included as part of the size.
- uint8_t variables_[0];
- } dynamic_;
- } captured_[0];
- // captured_ will always consist of one array element at runtime.
- // Set to [0] so that 'size_' is not counted in sizeof(Closure).
-
- friend class ClosureBuilder;
+ // NOTE: Actual fields are declared in ClosureStorage.
friend class ClosureTest;
};
+// ABI guarantees:
+// * Closure same size as a ClosureStorage
+// * ClosureStorage begins at the same point a Closure would begin.
+static_assert(sizeof(Closure) == sizeof(ClosureStorage), "Closure size must match ClosureStorage");
+
} // namespace lambda
} // namespace art
diff --git a/runtime/lambda/closure_builder.cc b/runtime/lambda/closure_builder.cc
index 739e965..7b36042 100644
--- a/runtime/lambda/closure_builder.cc
+++ b/runtime/lambda/closure_builder.cc
@@ -75,7 +75,7 @@
if (LIKELY(is_dynamic_size_ == false)) {
// Write in the extra bytes to store the dynamic size the first time.
is_dynamic_size_ = true;
- size_ += sizeof(Closure::captured_[0].dynamic_.size_);
+ size_ += sizeof(ClosureStorage::captured_[0].dynamic_.size_);
}
// A closure may be sized dynamically, so always query it for the true size.
@@ -107,38 +107,40 @@
<< "number of variables captured at runtime does not match "
<< "number of variables captured at compile time";
- Closure* closure = new (memory) Closure;
- closure->lambda_info_ = target_method;
+ ClosureStorage* closure_storage = new (memory) ClosureStorage;
+ closure_storage->lambda_info_ = target_method;
- static_assert(offsetof(Closure, captured_) == kInitialSize, "wrong initial size");
+ static_assert(offsetof(ClosureStorage, captured_) == kInitialSize, "wrong initial size");
size_t written_size;
if (UNLIKELY(is_dynamic_size_)) {
// The closure size must be set dynamically (i.e. nested lambdas).
- closure->captured_[0].dynamic_.size_ = GetSize();
- size_t header_size = offsetof(Closure, captured_[0].dynamic_.variables_);
+ closure_storage->captured_[0].dynamic_.size_ = GetSize();
+ size_t header_size = offsetof(ClosureStorage, captured_[0].dynamic_.variables_);
DCHECK_LE(header_size, GetSize());
size_t variables_size = GetSize() - header_size;
written_size =
WriteValues(target_method,
- closure->captured_[0].dynamic_.variables_,
+ closure_storage->captured_[0].dynamic_.variables_,
header_size,
variables_size);
} else {
// The closure size is known statically (i.e. no nested lambdas).
DCHECK(GetSize() == target_method->GetStaticClosureSize());
- size_t header_size = offsetof(Closure, captured_[0].static_variables_);
+ size_t header_size = offsetof(ClosureStorage, captured_[0].static_variables_);
DCHECK_LE(header_size, GetSize());
size_t variables_size = GetSize() - header_size;
written_size =
WriteValues(target_method,
- closure->captured_[0].static_variables_,
+ closure_storage->captured_[0].static_variables_,
header_size,
variables_size);
}
- DCHECK_EQ(written_size, closure->GetSize());
+ // OK: The closure storage is guaranteed to be the same as a closure.
+ Closure* closure = reinterpret_cast<Closure*>(closure_storage);
+ DCHECK_EQ(written_size, closure->GetSize());
return closure;
}
diff --git a/runtime/lambda/shorty_field_type.h b/runtime/lambda/shorty_field_type.h
index 46ddaa9..54bb4d4 100644
--- a/runtime/lambda/shorty_field_type.h
+++ b/runtime/lambda/shorty_field_type.h
@@ -285,6 +285,39 @@
}
}
+ // Get the number of virtual registers necessary to represent this type as a stack local.
+ inline size_t GetVirtualRegisterCount() const {
+ if (IsPrimitiveNarrow()) {
+ return 1;
+ } else if (IsPrimitiveWide()) {
+ return 2;
+ } else if (IsObject()) {
+ return kObjectReferenceSize / sizeof(uint32_t);
+ } else if (IsLambda()) {
+ return 2;
+ } else {
+ DCHECK(false) << "unknown shorty field type '" << static_cast<char>(value_) << "'";
+ UNREACHABLE();
+ }
+ }
+
+ // Count how many virtual registers would be necessary in order to store this list of shorty
+ // field types.
+ inline size_t static CountVirtualRegistersRequired(const char* shorty) {
+ size_t size = 0;
+
+ while (shorty != nullptr && *shorty != '\0') {
+ // Each argument appends to the size.
+ ShortyFieldType shorty_field{*shorty}; // NOLINT [readability/braces] [4]
+
+ size += shorty_field.GetVirtualRegisterCount();
+
+ ++shorty;
+ }
+
+ return size;
+ }
+
// Implicitly convert to the anonymous nested inner type. Used for exhaustive switch detection.
inline operator decltype(kByte)() const {
return value_;
diff --git a/runtime/lambda/shorty_field_type_test.cc b/runtime/lambda/shorty_field_type_test.cc
index 32bade9..430e39e 100644
--- a/runtime/lambda/shorty_field_type_test.cc
+++ b/runtime/lambda/shorty_field_type_test.cc
@@ -218,6 +218,56 @@
}
} // TEST_F
+TEST_F(ShortyFieldTypeTest, TestCalculateVRegSize) {
+ // Make sure the single calculation for each value is correct.
+ std::pair<size_t, char> expected_actual_single[] = {
+ // Primitives
+ { 1u, 'Z' },
+ { 1u, 'B' },
+ { 1u, 'C' },
+ { 1u, 'S' },
+ { 1u, 'I' },
+ { 1u, 'F' },
+ { 2u, 'J' },
+ { 2u, 'D' },
+ // Non-primitives
+ { 1u, 'L' },
+ { 2u, '\\' },
+ };
+
+ for (auto pair : expected_actual_single) {
+ SCOPED_TRACE(pair.second);
+ EXPECT_EQ(pair.first, ShortyFieldType(pair.second).GetVirtualRegisterCount());
+ }
+
+ // Make sure we are correctly calculating how many virtual registers a shorty descriptor takes.
+ std::pair<size_t, const char*> expected_actual[] = {
+ // Empty list
+ { 0u, "" },
+ // Primitives
+ { 1u, "Z" },
+ { 1u, "B" },
+ { 1u, "C" },
+ { 1u, "S" },
+ { 1u, "I" },
+ { 1u, "F" },
+ { 2u, "J" },
+ { 2u, "D" },
+ // Non-primitives
+ { 1u, "L" },
+ { 2u, "\\" },
+ // Multiple things at once:
+ { 10u, "ZBCSIFJD" },
+ { 5u, "LLSSI" },
+ { 6u, "LLL\\L" }
+ };
+
+ for (auto pair : expected_actual) {
+ SCOPED_TRACE(pair.second);
+ EXPECT_EQ(pair.first, ShortyFieldType::CountVirtualRegistersRequired(pair.second));
+ }
+} // TEST_F
+
// Helper class to probe a shorty's characteristics by minimizing copy-and-paste tests.
template <typename T, decltype(ShortyFieldType::kByte) kShortyEnum>
struct ShortyTypeCharacteristics {
diff --git a/runtime/lambda_proxy_test.cc b/runtime/lambda_proxy_test.cc
new file mode 100644
index 0000000..63d6ccc
--- /dev/null
+++ b/runtime/lambda_proxy_test.cc
@@ -0,0 +1,367 @@
+/*
+ * 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 <jni.h>
+#include <vector>
+
+#include "art_field-inl.h"
+#include "class_linker-inl.h"
+#include "compiler_callbacks.h"
+#include "common_compiler_test.h"
+#include "mirror/field-inl.h"
+#include "mirror/lambda_proxy.h"
+#include "mirror/method.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+
+// The enclosing class of all the interfaces used by this test.
+// -- Defined as a macro to allow for string concatenation.
+#define TEST_INTERFACE_ENCLOSING_CLASS_NAME "LambdaInterfaces"
+// Generate out "LLambdaInterfaces$<<iface>>;" , replacing <<iface>> with the interface name.
+#define MAKE_TEST_INTERFACE_NAME(iface) ("L" TEST_INTERFACE_ENCLOSING_CLASS_NAME "$" iface ";")
+
+#define ASSERT_NOT_NULL(x) ASSERT_TRUE((x) != nullptr)
+#define ASSERT_NULL(x) ASSERT_TRUE((x) == nullptr)
+#define EXPECT_NULL(x) EXPECT_TRUE((x) == nullptr)
+
+class LambdaProxyTest // : public CommonCompilerTest {
+ : public CommonRuntimeTest {
+ public:
+ // Generate a lambda proxy class with the given name and interfaces. This is a simplification from what
+ // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and
+ // we do not declare exceptions.
+ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa,
+ jobject jclass_loader,
+ const char* class_name,
+ const std::vector<mirror::Class*>& interfaces)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ CHECK(class_name != nullptr);
+ CHECK(jclass_loader != nullptr);
+
+ mirror::Class* java_lang_object =
+ class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+ CHECK(java_lang_object != nullptr);
+
+ jclass java_lang_class = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass());
+
+ // Builds the interfaces array.
+ jobjectArray proxy_class_interfaces = soa.Env()->NewObjectArray(interfaces.size(),
+ java_lang_class,
+ nullptr); // No initial element.
+ soa.Self()->AssertNoPendingException();
+ for (size_t i = 0; i < interfaces.size(); ++i) {
+ soa.Env()->SetObjectArrayElement(proxy_class_interfaces,
+ i,
+ soa.AddLocalReference<jclass>(interfaces[i]));
+ }
+
+ // Builds the method array.
+ jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString.
+ for (mirror::Class* interface : interfaces) {
+ methods_count += interface->NumVirtualMethods();
+ }
+ jobjectArray proxy_class_methods =
+ soa.Env()->NewObjectArray(methods_count,
+ soa.AddLocalReference<jclass>(mirror::Method::StaticClass()),
+ nullptr); // No initial element.
+ soa.Self()->AssertNoPendingException();
+
+ jsize array_index = 0;
+
+ //
+ // Fill the method array with the Object and all the interface's virtual methods.
+ //
+
+ // Add a method to 'proxy_class_methods'
+ auto add_method_to_array = [&](ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
+ CHECK(method != nullptr);
+ soa.Env()->SetObjectArrayElement(proxy_class_methods,
+ array_index++,
+ soa.AddLocalReference<jobject>(
+ mirror::Method::CreateFromArtMethod(soa.Self(),
+ method))
+ ); // NOLINT: [whitespace/parens] [2]
+
+ LOG(DEBUG) << "Add " << PrettyMethod(method) << " to list of methods to generate proxy";
+ };
+ // Add a method to 'proxy_class_methods' by looking it up from java.lang.Object
+ auto add_method_to_array_by_lookup = [&](const char* name, const char* method_descriptor)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* method = java_lang_object->FindDeclaredVirtualMethod(name,
+ method_descriptor,
+ sizeof(void*));
+ add_method_to_array(method);
+ };
+
+ // Add all methods from Object.
+ add_method_to_array_by_lookup("equals", "(Ljava/lang/Object;)Z");
+ add_method_to_array_by_lookup("hashCode", "()I");
+ add_method_to_array_by_lookup("toString", "()Ljava/lang/String;");
+
+ // Now adds all interfaces virtual methods.
+ for (mirror::Class* interface : interfaces) {
+ mirror::Class* next_class = interface;
+ do {
+ for (ArtMethod& method : next_class->GetVirtualMethods(sizeof(void*))) {
+ add_method_to_array(&method);
+ }
+ next_class = next_class->GetSuperClass();
+ } while (!next_class->IsObjectClass());
+ // Skip adding any methods from "Object".
+ }
+ CHECK_EQ(array_index, methods_count);
+
+ // Builds an empty exception array.
+ jobjectArray proxy_class_throws = soa.Env()->NewObjectArray(0 /* length */,
+ java_lang_class,
+ nullptr /* initial element*/);
+ soa.Self()->AssertNoPendingException();
+
+ bool already_exists;
+ mirror::Class* proxy_class =
+ class_linker_->CreateLambdaProxyClass(soa,
+ soa.Env()->NewStringUTF(class_name),
+ proxy_class_interfaces,
+ jclass_loader,
+ proxy_class_methods,
+ proxy_class_throws,
+ /*out*/&already_exists);
+
+ CHECK(!already_exists);
+
+ soa.Self()->AssertNoPendingException();
+ return proxy_class;
+ }
+
+ LambdaProxyTest() {
+ }
+
+ virtual void SetUp() {
+ CommonRuntimeTest::SetUp();
+ }
+
+ virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {
+ // Do not have any compiler options because we don't want to run as an AOT
+ // (In particular the lambda proxy class generation isn't currently supported for AOT).
+ this->callbacks_.reset();
+ }
+
+ template <typename THandleScope>
+ Handle<mirror::Class> GenerateProxyClass(THandleScope& hs,
+ const char* name,
+ const std::vector<mirror::Class*>& interfaces)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return hs.NewHandle(GenerateProxyClass(*soa_, jclass_loader_, name, interfaces));
+ }
+
+ protected:
+ ScopedObjectAccess* soa_ = nullptr;
+ jobject jclass_loader_ = nullptr;
+};
+
+// Creates a lambda proxy class and check ClassHelper works correctly.
+TEST_F(LambdaProxyTest, ProxyClassHelper) {
+ // gLogVerbosity.class_linker = true; // Uncomment to enable class linker logging.
+
+ ASSERT_NOT_NULL(Thread::Current());
+
+ ScopedObjectAccess soa(Thread::Current());
+ soa_ = &soa;
+
+ // Must happen after CommonRuntimeTest finishes constructing the runtime.
+ jclass_loader_ = LoadDex(TEST_INTERFACE_ENCLOSING_CLASS_NAME);
+ jobject jclass_loader = jclass_loader_;
+
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+ Handle<mirror::Class> J(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), MAKE_TEST_INTERFACE_NAME("J"), class_loader)));
+ ASSERT_TRUE(J.Get() != nullptr);
+
+ std::vector<mirror::Class*> interfaces;
+ interfaces.push_back(J.Get());
+ Handle<mirror::Class> proxy_class(hs.NewHandle(
+ GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
+ interfaces.clear(); // Don't least possibly stale objects in the array as good practice.
+ ASSERT_TRUE(proxy_class.Get() != nullptr);
+ ASSERT_TRUE(proxy_class->IsLambdaProxyClass());
+ ASSERT_TRUE(proxy_class->IsInitialized());
+
+ EXPECT_EQ(1U, proxy_class->NumDirectInterfaces()); // LambdaInterfaces$J.
+ EXPECT_EQ(J.Get(), mirror::Class::GetDirectInterface(soa.Self(), proxy_class, 0));
+ std::string temp;
+ const char* proxy_class_descriptor = proxy_class->GetDescriptor(&temp);
+ EXPECT_STREQ("L$Proxy1234;", proxy_class_descriptor);
+ EXPECT_EQ(nullptr, proxy_class->GetSourceFile());
+
+ // Make sure all the virtual methods are marked as a proxy
+ for (ArtMethod& method : proxy_class->GetVirtualMethods(sizeof(void*))) {
+ SCOPED_TRACE(PrettyMethod(&method, /* with_signature */true));
+ EXPECT_TRUE(method.IsProxyMethod());
+ EXPECT_TRUE(method.IsLambdaProxyMethod());
+ EXPECT_FALSE(method.IsReflectProxyMethod());
+ }
+}
+
+// Creates a proxy class and check FieldHelper works correctly.
+TEST_F(LambdaProxyTest, ProxyFieldHelper) {
+ // gLogVerbosity.class_linker = true; // Uncomment to enable class linker logging.
+
+ ASSERT_NOT_NULL(Thread::Current());
+
+ ScopedObjectAccess soa(Thread::Current());
+ soa_ = &soa;
+
+ // Must happen after CommonRuntimeTest finishes constructing the runtime.
+ jclass_loader_ = LoadDex(TEST_INTERFACE_ENCLOSING_CLASS_NAME);
+ jobject jclass_loader = jclass_loader_;
+
+ StackHandleScope<9> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+ Handle<mirror::Class> I(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), MAKE_TEST_INTERFACE_NAME("I"), class_loader)));
+ ASSERT_NOT_NULL(I.Get());
+
+ // Create the lambda proxy which implements interfaces "I".
+ Handle<mirror::Class> proxy_class = GenerateProxyClass(hs,
+ "$Proxy1234",
+ { I.Get() }); // Interfaces.
+
+ ASSERT_NOT_NULL(proxy_class.Get());
+ EXPECT_TRUE(proxy_class->IsLambdaProxyClass());
+ EXPECT_TRUE(proxy_class->IsInitialized());
+ EXPECT_NULL(proxy_class->GetIFieldsPtr());
+
+ LengthPrefixedArray<ArtField>* static_fields = proxy_class->GetSFieldsPtr();
+ ASSERT_NOT_NULL(static_fields);
+
+ // Must have "throws" and "interfaces" static fields.
+ ASSERT_EQ(+mirror::LambdaProxy::kStaticFieldCount, proxy_class->NumStaticFields());
+
+ static constexpr const char* kInterfacesClassName = "[Ljava/lang/Class;";
+ static constexpr const char* kThrowsClassName = "[[Ljava/lang/Class;";
+
+ // Class for "interfaces" field.
+ Handle<mirror::Class> interfaces_field_class =
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), kInterfacesClassName));
+ ASSERT_NOT_NULL(interfaces_field_class.Get());
+
+ // Class for "throws" field.
+ Handle<mirror::Class> throws_field_class =
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), kThrowsClassName));
+ ASSERT_NOT_NULL(throws_field_class.Get());
+
+ // Helper to test the static fields for correctness.
+ auto test_static_field = [&](size_t index,
+ const char* field_name,
+ Handle<mirror::Class>& handle_class,
+ const char* class_name)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtField* field = &static_fields->At(index);
+ EXPECT_STREQ(field_name, field->GetName());
+ EXPECT_STREQ(class_name, field->GetTypeDescriptor());
+ EXPECT_EQ(handle_class.Get(), field->GetType</*kResolve*/true>())
+ << "Expected: " << PrettyClass(interfaces_field_class.Get()) << ", "
+ << "Actual: " << PrettyClass(field->GetType</*kResolve*/true>()) << ", "
+ << "field_name: " << field_name;
+ std::string temp;
+ EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp));
+ EXPECT_FALSE(field->IsPrimitiveType());
+ };
+
+ // Test "Class[] interfaces" field.
+ test_static_field(mirror::LambdaProxy::kStaticFieldIndexInterfaces,
+ "interfaces",
+ interfaces_field_class,
+ kInterfacesClassName);
+
+ // Test "Class[][] throws" field.
+ test_static_field(mirror::LambdaProxy::kStaticFieldIndexThrows,
+ "throws",
+ throws_field_class,
+ kThrowsClassName);
+}
+
+// Creates two proxy classes and check the art/mirror fields of their static fields.
+TEST_F(LambdaProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) {
+ // gLogVerbosity.class_linker = true; // Uncomment to enable class linker logging.
+
+ ASSERT_NOT_NULL(Thread::Current());
+
+ ScopedObjectAccess soa(Thread::Current());
+ soa_ = &soa;
+
+ // Must happen after CommonRuntimeTest finishes constructing the runtime.
+ jclass_loader_ = LoadDex(TEST_INTERFACE_ENCLOSING_CLASS_NAME);
+ jobject jclass_loader = jclass_loader_;
+
+ StackHandleScope<8> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+ Handle<mirror::Class> proxyClass0;
+ Handle<mirror::Class> proxyClass1;
+ {
+ Handle<mirror::Class> L(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), MAKE_TEST_INTERFACE_NAME("L"), class_loader)));
+ ASSERT_TRUE(L.Get() != nullptr);
+
+ std::vector<mirror::Class*> interfaces = { L.Get() };
+ proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces));
+ proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
+ }
+
+ ASSERT_TRUE(proxyClass0.Get() != nullptr);
+ ASSERT_TRUE(proxyClass0->IsLambdaProxyClass());
+ ASSERT_TRUE(proxyClass0->IsInitialized());
+ ASSERT_TRUE(proxyClass1.Get() != nullptr);
+ ASSERT_TRUE(proxyClass1->IsLambdaProxyClass());
+ ASSERT_TRUE(proxyClass1->IsInitialized());
+
+ LengthPrefixedArray<ArtField>* static_fields0 = proxyClass0->GetSFieldsPtr();
+ ASSERT_TRUE(static_fields0 != nullptr);
+ ASSERT_EQ(2u, static_fields0->size());
+ LengthPrefixedArray<ArtField>* static_fields1 = proxyClass1->GetSFieldsPtr();
+ ASSERT_TRUE(static_fields1 != nullptr);
+ ASSERT_EQ(2u, static_fields1->size());
+
+ EXPECT_EQ(static_fields0->At(0).GetDeclaringClass(), proxyClass0.Get());
+ EXPECT_EQ(static_fields0->At(1).GetDeclaringClass(), proxyClass0.Get());
+ EXPECT_EQ(static_fields1->At(0).GetDeclaringClass(), proxyClass1.Get());
+ EXPECT_EQ(static_fields1->At(1).GetDeclaringClass(), proxyClass1.Get());
+
+ Handle<mirror::Field> field00 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0->At(0), true));
+ Handle<mirror::Field> field01 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0->At(1), true));
+ Handle<mirror::Field> field10 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1->At(0), true));
+ Handle<mirror::Field> field11 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1->At(1), true));
+ EXPECT_EQ(field00->GetArtField(), &static_fields0->At(0));
+ EXPECT_EQ(field01->GetArtField(), &static_fields0->At(1));
+ EXPECT_EQ(field10->GetArtField(), &static_fields1->At(0));
+ EXPECT_EQ(field11->GetArtField(), &static_fields1->At(1));
+}
+
+// TODO: make sure there's a non-abstract implementation of the single-abstract-method on the class.
+
+} // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 9e416dc..a8685b8 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -695,7 +695,11 @@
}
inline const DexFile& Class::GetDexFile() {
- return *GetDexCache()->GetDexFile();
+ DexCache* dex_cache = GetDexCache();
+ DCHECK(dex_cache != nullptr);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ DCHECK(dex_file != nullptr);
+ return *dex_file;
}
inline bool Class::DescriptorEquals(const char* match) {
@@ -703,8 +707,8 @@
return match[0] == '[' && GetComponentType()->DescriptorEquals(match + 1);
} else if (IsPrimitive()) {
return strcmp(Primitive::Descriptor(GetPrimitiveType()), match) == 0;
- } else if (IsProxyClass()) {
- return ProxyDescriptorEquals(match);
+ } else if (IsAnyProxyClass()) {
+ return AnyProxyDescriptorEquals(match);
} else {
const DexFile& dex_file = GetDexFile();
const DexFile::TypeId& type_id = dex_file.GetTypeId(GetClassDef()->class_idx_);
@@ -720,22 +724,32 @@
}
}
-inline ObjectArray<Class>* Class::GetInterfaces() {
- CHECK(IsProxyClass());
+inline ObjectArray<Class>* Class::GetInterfacesForAnyProxy() {
+ CHECK(IsAnyProxyClass());
// First static field.
auto* field = GetStaticField(0);
DCHECK_STREQ(field->GetName(), "interfaces");
MemberOffset field_offset = field->GetOffset();
- return GetFieldObject<ObjectArray<Class>>(field_offset);
+ ObjectArray<Class>* interfaces_array = GetFieldObject<ObjectArray<Class>>(field_offset);
+
+ CHECK(interfaces_array != nullptr);
+ if (UNLIKELY(IsLambdaProxyClass())) {
+ DCHECK_EQ(1, interfaces_array->GetLength())
+ << "Lambda proxies cannot have multiple direct interfaces implemented";
+ }
+ return interfaces_array;
}
-inline ObjectArray<ObjectArray<Class>>* Class::GetThrows() {
- CHECK(IsProxyClass());
+inline ObjectArray<ObjectArray<Class>>* Class::GetThrowsForAnyProxy() {
+ CHECK(IsAnyProxyClass());
// Second static field.
auto* field = GetStaticField(1);
DCHECK_STREQ(field->GetName(), "throws");
+
MemberOffset field_offset = field->GetOffset();
- return GetFieldObject<ObjectArray<ObjectArray<Class>>>(field_offset);
+ auto* throws_array = GetFieldObject<ObjectArray<ObjectArray<Class>>>(field_offset);
+ CHECK(throws_array != nullptr);
+ return throws_array;
}
inline MemberOffset Class::GetDisableIntrinsicFlagOffset() {
@@ -796,8 +810,8 @@
return 0;
} else if (IsArrayClass()) {
return 2;
- } else if (IsProxyClass()) {
- mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
+ } else if (IsAnyProxyClass()) {
+ mirror::ObjectArray<mirror::Class>* interfaces = GetInterfacesForAnyProxy();
return interfaces != nullptr ? interfaces->GetLength() : 0;
} else {
const DexFile::TypeList* interfaces = GetInterfaceTypeList();
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 05a9039..b201293 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -538,6 +538,7 @@
ArtMethod* Class::FindClassInitializer(size_t pointer_size) {
for (ArtMethod& method : GetDirectMethods(pointer_size)) {
+ DCHECK(reinterpret_cast<volatile void*>(&method) != nullptr);
if (method.IsClassInitializer()) {
DCHECK_STREQ(method.GetName(), "<clinit>");
DCHECK_STREQ(method.GetSignature().ToString().c_str(), "()V");
@@ -742,8 +743,8 @@
return Primitive::Descriptor(GetPrimitiveType());
} else if (IsArrayClass()) {
return GetArrayDescriptor(storage);
- } else if (IsProxyClass()) {
- *storage = Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(this);
+ } else if (IsAnyProxyClass()) {
+ *storage = Runtime::Current()->GetClassLinker()->GetDescriptorForAnyProxy(this);
return storage->c_str();
} else {
const DexFile& dex_file = GetDexFile();
@@ -786,8 +787,10 @@
DCHECK_EQ(1U, idx);
return class_linker->FindSystemClass(self, "Ljava/io/Serializable;");
}
- } else if (klass->IsProxyClass()) {
- mirror::ObjectArray<mirror::Class>* interfaces = klass.Get()->GetInterfaces();
+ } else if (klass->IsAnyProxyClass()) {
+ // Proxies don't have a dex cache, so look at the
+ // interfaces through the magic static field "interfaces" from the proxy class itself.
+ mirror::ObjectArray<mirror::Class>* interfaces = klass.Get()->GetInterfacesForAnyProxy();
DCHECK(interfaces != nullptr);
return interfaces->Get(idx);
} else {
@@ -826,7 +829,7 @@
std::string Class::GetLocation() {
mirror::DexCache* dex_cache = GetDexCache();
- if (dex_cache != nullptr && !IsProxyClass()) {
+ if (dex_cache != nullptr && !IsAnyProxyClass()) {
return dex_cache->GetLocation()->ToModifiedUtf8();
}
// Arrays and proxies are generated and have no corresponding dex file location.
@@ -944,9 +947,9 @@
return new_class->AsClass();
}
-bool Class::ProxyDescriptorEquals(const char* match) {
- DCHECK(IsProxyClass());
- return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(this) == match;
+bool Class::AnyProxyDescriptorEquals(const char* match) {
+ DCHECK(IsAnyProxyClass());
+ return Runtime::Current()->GetClassLinker()->GetDescriptorForAnyProxy(this) == match;
}
// TODO: Move this to java_lang_Class.cc?
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 0ab5b97..fcfb4b9 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -352,8 +352,16 @@
static String* ComputeName(Handle<Class> h_this) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
+ // Is this either a java.lang.reflect.Proxy or a boxed lambda (java.lang.LambdaProxy)?
+ // -- Most code doesn't need to make the distinction, and this is the preferred thing to check.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool IsProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool IsAnyProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return IsReflectProxyClass() || IsLambdaProxyClass();
+ }
+
+ // Is this a java.lang.reflect.Proxy ?
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsReflectProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
// Read access flags without using getter as whether something is a proxy can be check in
// any loaded state
// TODO: switch to a check if the super class is java.lang.reflect.Proxy?
@@ -361,6 +369,17 @@
return (access_flags & kAccClassIsProxy) != 0;
}
+ // Is this a boxed lambda (java.lang.LambdaProxy)?
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsLambdaProxyClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ // Read access flags without using getter as whether something is a proxy can be check in
+ // any loaded state
+ // TODO: switch to a check if the super class is java.lang.reflect.Proxy?
+ uint32_t access_flags = GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+ return (access_flags & kAccClassIsLambdaProxy) != 0;
+ }
+
+
static MemberOffset PrimitiveTypeOffset() {
return OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_);
}
@@ -677,6 +696,8 @@
return MemberOffset(OFFSETOF_MEMBER(Class, super_class_));
}
+ // Returns the class's ClassLoader.
+ // A null value is returned if and only if this is a boot classpath class.
ClassLoader* GetClassLoader() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_);
void SetClassLoader(ClassLoader* new_cl) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1076,6 +1097,8 @@
bool DescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_);
+ // Returns the backing DexFile's class definition for this class.
+ // This returns null if and only if the class has no backing DexFile.
const DexFile::ClassDef* GetClassDef() SHARED_REQUIRES(Locks::mutator_lock_);
ALWAYS_INLINE uint32_t NumDirectInterfaces() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1102,11 +1125,15 @@
size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
- // For proxy class only.
- ObjectArray<Class>* GetInterfaces() SHARED_REQUIRES(Locks::mutator_lock_);
+ // For any proxy class only. Returns list of directly implemented interfaces.
+ // The value returned is always non-null.
+ ObjectArray<Class>* GetInterfacesForAnyProxy() SHARED_REQUIRES(Locks::mutator_lock_);
- // For proxy class only.
- ObjectArray<ObjectArray<Class>>* GetThrows() SHARED_REQUIRES(Locks::mutator_lock_);
+ // For any proxy class only. Returns a 2d array of classes.
+ // -- The 0th dimension correponds to the vtable index.
+ // -- The 1st dimension is a list of checked exception classes.
+ // The value returned is always non-null.
+ ObjectArray<ObjectArray<Class>>* GetThrowsForAnyProxy() SHARED_REQUIRES(Locks::mutator_lock_);
// For reference class only.
MemberOffset GetDisableIntrinsicFlagOffset() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1194,7 +1221,7 @@
IterationRange<StrideIterator<ArtField>> GetIFieldsUnchecked()
SHARED_REQUIRES(Locks::mutator_lock_);
- bool ProxyDescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_);
+ bool AnyProxyDescriptorEquals(const char* match) SHARED_REQUIRES(Locks::mutator_lock_);
// Check that the pointer size matches the one in the class linker.
ALWAYS_INLINE static void CheckPointerSize(size_t pointer_size);
diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h
index e22ddd7..3139117 100644
--- a/runtime/mirror/class_loader-inl.h
+++ b/runtime/mirror/class_loader-inl.h
@@ -21,6 +21,7 @@
#include "base/mutex-inl.h"
#include "class_table-inl.h"
+#include "lambda/box_class_table-inl.h"
namespace art {
namespace mirror {
@@ -35,6 +36,10 @@
if (class_table != nullptr) {
class_table->VisitRoots(visitor);
}
+ lambda::BoxClassTable* const lambda_box_class_table = GetLambdaProxyCache();
+ if (lambda_box_class_table != nullptr) {
+ lambda_box_class_table->VisitRoots(visitor);
+ }
}
} // namespace mirror
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index c2a65d6..9d4fe96 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -24,6 +24,12 @@
struct ClassLoaderOffsets;
class ClassTable;
+namespace lambda {
+
+class BoxClassTable;
+
+} // namespace lambda
+
namespace mirror {
class Class;
@@ -60,6 +66,16 @@
reinterpret_cast<uint64_t>(allocator));
}
+ lambda::BoxClassTable* GetLambdaProxyCache() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return reinterpret_cast<lambda::BoxClassTable*>(
+ GetField64(OFFSET_OF_OBJECT_MEMBER(ClassLoader, lambda_proxy_cache_)));
+ }
+
+ void SetLambdaProxyCache(lambda::BoxClassTable* cache) SHARED_REQUIRES(Locks::mutator_lock_) {
+ SetField64<false>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, lambda_proxy_cache_),
+ reinterpret_cast<uint64_t>(cache));
+ }
+
private:
// Visit instance fields of the class loader as well as its associated classes.
// Null class loader is handled by ClassLinker::VisitClassRoots.
@@ -76,6 +92,7 @@
uint32_t padding_ ATTRIBUTE_UNUSED;
uint64_t allocator_;
uint64_t class_table_;
+ uint64_t lambda_proxy_cache_;
friend struct art::ClassLoaderOffsets; // for verifying offset information
friend class Object; // For VisitReferences
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index 8a0daec..49c443e 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -57,14 +57,15 @@
const auto pointer_size = kTransactionActive ?
Runtime::Current()->GetClassLinker()->GetImagePointerSize() : sizeof(void*);
auto dex_field_index = field->GetDexFieldIndex();
- auto* resolved_field = field->GetDexCache()->GetResolvedField(dex_field_index, pointer_size);
- if (field->GetDeclaringClass()->IsProxyClass()) {
+ if (field->GetDeclaringClass()->IsAnyProxyClass()) {
DCHECK(field->IsStatic());
DCHECK_LT(dex_field_index, 2U);
// The two static fields (interfaces, throws) of all proxy classes
// share the same dex file indices 0 and 1. So, we can't resolve
// them in the dex cache.
} else {
+ ArtField* resolved_field =
+ field->GetDexCache()->GetResolvedField(dex_field_index, pointer_size);
if (resolved_field != nullptr) {
DCHECK_EQ(resolved_field, field);
} else {
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index ff6847c..b02e5b5 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -56,7 +56,7 @@
ArtField* Field::GetArtField() {
mirror::Class* declaring_class = GetDeclaringClass();
- if (UNLIKELY(declaring_class->IsProxyClass())) {
+ if (UNLIKELY(declaring_class->IsAnyProxyClass())) {
DCHECK(IsStatic());
DCHECK_EQ(declaring_class->NumStaticFields(), 2U);
// 0 == Class[] interfaces; 1 == Class[][] throws;
diff --git a/runtime/mirror/lambda_proxy.h b/runtime/mirror/lambda_proxy.h
new file mode 100644
index 0000000..cff3a12
--- /dev/null
+++ b/runtime/mirror/lambda_proxy.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_LAMBDA_PROXY_H_
+#define ART_RUNTIME_MIRROR_LAMBDA_PROXY_H_
+
+#include "lambda/closure.h"
+#include "object.h"
+
+namespace art {
+
+struct LambdaProxyOffsets;
+
+namespace mirror {
+
+// C++ mirror of a lambda proxy. Does not yet have a Java-equivalent source file.
+class MANAGED LambdaProxy FINAL : public Object {
+ public:
+ // Note that the runtime subclasses generate the following static fields:
+
+ // private static java.lang.Class[] interfaces; // Declared interfaces for the lambda interface.
+ static constexpr size_t kStaticFieldIndexInterfaces = 0;
+ // private static java.lang.Class[][] throws; // Maps vtable id to list of classes.
+ static constexpr size_t kStaticFieldIndexThrows = 1;
+ static constexpr size_t kStaticFieldCount = 2; // Number of fields total.
+
+ // The offset from the start of 'LambdaProxy' object, to the closure_ field, in bytes.
+ // -- This is exposed publically in order to avoid exposing 'closure_' publically.
+ // -- Only meant to be used in stubs and other compiled code, not in runtime.
+ static inline MemberOffset GetInstanceFieldOffsetClosure() {
+ return OFFSET_OF_OBJECT_MEMBER(LambdaProxy, closure_);
+ }
+
+ // Direct methods available on the class:
+ static constexpr size_t kDirectMethodIndexConstructor = 0; // <init>()V
+ static constexpr size_t kDirectMethodCount = 1; // Only the constructor.
+
+ // Accessors to the fields:
+
+ // Get the native closure pointer. Usually non-null outside of lambda proxy contexts.
+ lambda::Closure* GetClosure() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return reinterpret_cast<lambda::Closure*>(
+ GetField64(GetInstanceFieldOffsetClosure()));
+ }
+
+ // Set the native closure pointer. Usually should be non-null outside of lambda proxy contexts.
+ void SetClosure(lambda::Closure* closure) SHARED_REQUIRES(Locks::mutator_lock_) {
+ SetField64<false>(GetInstanceFieldOffsetClosure(),
+ reinterpret_cast<uint64_t>(closure));
+ }
+
+ private:
+ // Instance fields, present in the base class and every generated subclass:
+
+ // private long closure;
+ union {
+ lambda::Closure* actual;
+ uint64_t padding; // Don't trip up GetObjectSize checks, since the Java code has a long.
+ } closure_;
+
+ // Friends for generating offset tests:
+ friend struct art::LambdaProxyOffsets; // for verifying offset information
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LambdaProxy);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_LAMBDA_PROXY_H_
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 9946eab..36aa57f 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -54,6 +54,8 @@
// if any particular method needs to be a default conflict. Used to figure out at runtime if
// invoking this method will throw an exception.
static constexpr uint32_t kAccDefaultConflict = 0x00800000; // method (runtime)
+// Set by the class linker when creating a class that's a subtype of LambdaProxy.
+static constexpr uint32_t kAccClassIsLambdaProxy = 0x01000000; // class (dex only)
// Special runtime-only flags.
// Interface and all its super-interfaces with default methods have been recursively initialized.
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 5e42392..6cebd4d 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -103,7 +103,7 @@
static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
ScopedFastNativeObjectAccess soa(env);
mirror::Class* c = DecodeClass(soa, javaThis);
- return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
+ return soa.AddLocalReference<jobjectArray>(c->GetInterfacesForAnyProxy()->Clone(soa.Self()));
}
static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
@@ -489,7 +489,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
@@ -501,7 +501,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
// Return an empty array instead of a null pointer.
mirror::Class* annotation_array_class =
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
@@ -517,7 +517,7 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
mirror::ObjectArray<mirror::Class>* classes = nullptr;
- if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) {
+ if (!klass->IsAnyProxyClass() && klass->GetDexCache() != nullptr) {
classes = klass->GetDexFile().GetDeclaredClasses(klass);
}
if (classes == nullptr) {
@@ -543,7 +543,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
return soa.AddLocalReference<jclass>(klass->GetDexFile().GetEnclosingClass(klass));
@@ -553,7 +553,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
mirror::Object* method = klass->GetDexFile().GetEnclosingMethod(klass);
@@ -570,7 +570,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
mirror::Object* method = klass->GetDexFile().GetEnclosingMethod(klass);
@@ -587,7 +587,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return defaultValue;
}
uint32_t flags;
@@ -601,7 +601,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
mirror::String* class_name = nullptr;
@@ -615,7 +615,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return false;
}
mirror::String* class_name = nullptr;
@@ -630,7 +630,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return false;
}
Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
@@ -641,7 +641,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ if (klass->IsAnyProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
// Return null for anonymous classes.
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index aac800a..9166ecc 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -419,7 +419,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
- if (field->GetDeclaringClass()->IsProxyClass()) {
+ if (field->GetDeclaringClass()->IsAnyProxyClass()) {
return nullptr;
}
Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
@@ -429,7 +429,7 @@
static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) {
ScopedFastNativeObjectAccess soa(env);
ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
- if (field->GetDeclaringClass()->IsProxyClass()) {
+ if (field->GetDeclaringClass()->IsAnyProxyClass()) {
// Return an empty array instead of a null pointer.
mirror::Class* annotation_array_class =
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
@@ -443,7 +443,7 @@
static jobjectArray Field_getSignatureAnnotation(JNIEnv* env, jobject javaField) {
ScopedFastNativeObjectAccess soa(env);
ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
- if (field->GetDeclaringClass()->IsProxyClass()) {
+ if (field->GetDeclaringClass()->IsAnyProxyClass()) {
return nullptr;
}
return soa.AddLocalReference<jobjectArray>(
@@ -455,7 +455,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
- if (field->GetDeclaringClass()->IsProxyClass()) {
+ if (field->GetDeclaringClass()->IsAnyProxyClass()) {
return false;
}
Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index caacba6..7894c9b 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -32,7 +32,7 @@
static jobject Method_getAnnotationNative(JNIEnv* env, jobject javaMethod, jclass annotationType) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- if (method->GetDeclaringClass()->IsProxyClass()) {
+ if (method->GetDeclaringClass()->IsAnyProxyClass()) {
return nullptr;
}
StackHandleScope<1> hs(soa.Self());
@@ -44,7 +44,7 @@
static jobjectArray Method_getDeclaredAnnotations(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- if (method->GetDeclaringClass()->IsProxyClass()) {
+ if (method->GetDeclaringClass()->IsAnyProxyClass()) {
// Return an empty array instead of a null pointer.
mirror::Class* annotation_array_class =
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
@@ -67,7 +67,7 @@
static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- if (method->GetDeclaringClass()->IsProxyClass()) {
+ if (method->GetDeclaringClass()->IsAnyProxyClass()) {
mirror::Class* klass = method->GetDeclaringClass();
int throws_index = -1;
size_t i = 0;
@@ -79,7 +79,8 @@
++i;
}
CHECK_NE(throws_index, -1);
- mirror::ObjectArray<mirror::Class>* declared_exceptions = klass->GetThrows()->Get(throws_index);
+ mirror::ObjectArray<mirror::Class>* declared_exceptions =
+ klass->GetThrowsForAnyProxy()->Get(throws_index);
return soa.AddLocalReference<jobjectArray>(declared_exceptions->Clone(soa.Self()));
} else {
mirror::ObjectArray<mirror::Class>* result_array =
@@ -104,7 +105,7 @@
static jobjectArray Method_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- if (method->GetDeclaringClass()->IsProxyClass()) {
+ if (method->GetDeclaringClass()->IsAnyProxyClass()) {
return nullptr;
}
return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetParameterAnnotations(method));
@@ -120,7 +121,7 @@
jclass annotationType) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
- if (method->GetDeclaringClass()->IsProxyClass()) {
+ if (method->GetDeclaringClass()->IsAnyProxyClass()) {
return false;
}
StackHandleScope<1> hs(soa.Self());
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 4a6ab40..647cec0 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -27,15 +27,31 @@
namespace art {
static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces,
- jobject loader, jobjectArray methods, jobjectArray throws) {
+ jobject loader, jobjectArray methods, jobjectArray throws,
+ jboolean is_lambda_proxy) {
ScopedFastNativeObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- return soa.AddLocalReference<jclass>(class_linker->CreateProxyClass(
- soa, name, interfaces, loader, methods, throws));
+
+ mirror::Class* proxy_class = nullptr;
+
+ if (UNLIKELY(is_lambda_proxy)) {
+ bool already_exists; // XX: Perhaps add lambdaProxyCache to java.lang.ClassLoader ?
+ proxy_class = class_linker->CreateLambdaProxyClass(soa,
+ name,
+ interfaces,
+ loader,
+ methods,
+ throws,
+ /*out*/&already_exists);
+ } else {
+ proxy_class = class_linker->CreateProxyClass(soa, name, interfaces, loader, methods, throws);
+ }
+
+ return soa.AddLocalReference<jclass>(proxy_class);
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;)Ljava/lang/Class;"),
+ NATIVE_METHOD(Proxy, generateProxy, "!(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;Z)Ljava/lang/Class;"),
};
void register_java_lang_reflect_Proxy(JNIEnv* env) {
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 57472ad..57aafcc 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -121,7 +121,7 @@
GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
interfaces.clear(); // Don't least possibly stale objects in the array as good practice.
ASSERT_TRUE(proxy_class.Get() != nullptr);
- ASSERT_TRUE(proxy_class->IsProxyClass());
+ ASSERT_TRUE(proxy_class->IsReflectProxyClass());
ASSERT_TRUE(proxy_class->IsInitialized());
EXPECT_EQ(2U, proxy_class->NumDirectInterfaces()); // Interfaces$I and Interfaces$J.
@@ -157,7 +157,7 @@
}
ASSERT_TRUE(proxyClass.Get() != nullptr);
- ASSERT_TRUE(proxyClass->IsProxyClass());
+ ASSERT_TRUE(proxyClass->IsReflectProxyClass());
ASSERT_TRUE(proxyClass->IsInitialized());
EXPECT_TRUE(proxyClass->GetIFieldsPtr() == nullptr);
@@ -208,10 +208,10 @@
}
ASSERT_TRUE(proxyClass0.Get() != nullptr);
- ASSERT_TRUE(proxyClass0->IsProxyClass());
+ ASSERT_TRUE(proxyClass0->IsReflectProxyClass());
ASSERT_TRUE(proxyClass0->IsInitialized());
ASSERT_TRUE(proxyClass1.Get() != nullptr);
- ASSERT_TRUE(proxyClass1->IsProxyClass());
+ ASSERT_TRUE(proxyClass1->IsReflectProxyClass());
ASSERT_TRUE(proxyClass1->IsInitialized());
LengthPrefixedArray<ArtField>* static_fields0 = proxyClass0->GetSFieldsPtr();
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 9098d38..2ff9fd2 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -172,12 +172,23 @@
} else {
return cur_shadow_frame_->GetVRegReference(0);
}
- } else if (m->IsProxyMethod()) {
+ } else if (m->IsReflectProxyMethod()) {
if (cur_quick_frame_ != nullptr) {
return artQuickGetProxyThisObject(cur_quick_frame_);
} else {
return cur_shadow_frame_->GetVRegReference(0);
}
+ } else if (m->IsLambdaProxyMethod()) {
+ if (cur_quick_frame_ != nullptr) {
+ // XX: Should be safe to return null here, the lambda proxies
+ // don't set up their own quick frame because they don't need to spill any registers.
+ // By the time we are executing inside of the final target of the proxy invoke,
+ // the original 'this' reference is no longer live.
+ LOG(WARNING) << "Lambda proxies don't have a quick frame, do they?!";
+ return nullptr;
+ } else {
+ return cur_shadow_frame_->GetVRegReference(0);
+ }
} else {
const DexFile::CodeItem* code_item = m->GetCodeItem();
if (code_item == nullptr) {
@@ -814,7 +825,27 @@
// compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader.
DCHECK(!method->IsDirect() && !method->IsConstructor())
<< "Constructors of proxy classes must have a OatQuickMethodHeader";
- return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+
+ if (method->IsReflectProxyMethod()) {
+ return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+ } else if (method->IsLambdaProxyMethod()) {
+ // Set this to true later once every stub works without a frame.
+ // This is currently 'false' because using a closure as a "long"
+ // requires a quick frame to be set up on 32-bit architectures.
+ constexpr bool kLambdaProxyStubsSupportFrameless = false;
+ if (kIsDebugBuild || !kLambdaProxyStubsSupportFrameless) {
+ // When debugging we always use the 'RefAndArgs' quick frame to allow us
+ // to see a runtime stub when unwinding.
+ return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
+ } else {
+ // Lambda proxies don't bother setting up a quick frame for release builds.
+ LOG(FATAL) << "Requested QuickMethodFrameInfo for a lambda proxy,"
+ << "but it doesn't have one, for method: " << PrettyMethod(method);
+ UNREACHABLE();
+ }
+ } else {
+ LOG(FATAL) << "Unknown type of proxy method " << PrettyMethod(method);
+ }
}
// The only remaining case is if the method is native and uses the generic JNI stub.
diff --git a/test/955-lambda-smali/expected.txt b/test/955-lambda-smali/expected.txt
index 16381e4..8afe4bc 100644
--- a/test/955-lambda-smali/expected.txt
+++ b/test/955-lambda-smali/expected.txt
@@ -26,3 +26,5 @@
(CaptureVariables) (0-args, 1 captured variable 'D'): value is -Infinity
(CaptureVariables) (0-args, 8 captured variable 'ZBCSIJFD'): value is true,R,∂,1000,12345678,3287471278325742,Infinity,-Infinity
(CaptureVariables) Caught NPE
+(BoxInvoke) Hello boxing world! (0-args, no closure) void
+(BoxInvoke) Hello boxing world!(1-args, no closure) returned: 12345678
diff --git a/test/955-lambda-smali/smali/BoxInvoke.smali b/test/955-lambda-smali/smali/BoxInvoke.smali
new file mode 100644
index 0000000..8b53333
--- /dev/null
+++ b/test/955-lambda-smali/smali/BoxInvoke.smali
@@ -0,0 +1,103 @@
+# 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.
+#
+.class public LBoxInvoke;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static run()V
+ .registers 0
+
+ invoke-static {}, LBoxInvoke;->testBoxInvoke()V
+ invoke-static {}, LBoxInvoke;->forceGC()V
+
+ return-void
+.end method
+
+# Test that invoke-virtual works on boxed innate lambdas.
+.method public static testBoxInvoke()V
+ .registers 100
+
+ # Try invoking 0-arg void return lambda
+ create-lambda v0, LBoxInvoke;->doHelloWorld0(J)V
+ const-string v2, "Ljava/lang/Runnable;"
+ box-lambda v2, v0 # Ljava/lang/Runnable;
+ invoke-interface {v2}, Ljava/lang/Runnable;->run()V
+
+ # Try invoking 1-arg int return lambda
+ create-lambda v3, LBoxInvoke;->doHelloWorld1(JLjava/lang/Object;)I
+ const-string v5, "Ljava/lang/Comparable;"
+ box-lambda v5, v3 # Ljava/lang/Comparable;
+ const-string v6, "Hello boxing world!"
+ invoke-interface {v5, v6}, Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I
+ move-result v7
+ sget-object v8, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v8, v7}, Ljava/io/PrintStream;->println(I)V
+
+ return-void
+
+ # TODO: more tests once box-lambda can take a type descriptor.
+
+.end method
+
+#TODO: should use a closure type instead of a long.
+.method public static doHelloWorld0(J)V
+ .registers 4 # 1 wide parameters, 2 locals
+
+ const-string v0, "(BoxInvoke) Hello boxing world! (0-args, no closure) void"
+
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ return-void
+.end method
+
+#TODO: should use a closure type instead of a long.
+.method public static doHelloWorld1(JLjava/lang/Object;)I
+ # J = closure, L = obj, I = return type
+ .registers 6 # 1 wide parameters, 1 narrow parameter, 3 locals
+
+ # Prints "<before> $parameter1(Object) <after>:" without the line terminator.
+
+ const-string v0, "(BoxInvoke) "
+
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ # System.out.print("<before>");
+ invoke-virtual {v1, v0}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V
+
+ # System.out.print(obj);
+ invoke-virtual {v1, p2}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+ # System.out.print("<after>: ");
+ const-string v0, "(1-args, no closure) returned: "
+ invoke-virtual {v1, v0}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V
+
+ const v2, 12345678
+ return v2
+.end method
+
+# Force a GC. Used to ensure our weak reference table of boxed lambdas is getting swept.
+.method private static forceGC()V
+ .registers 1
+ invoke-static {}, Ljava/lang/Runtime;->getRuntime()Ljava/lang/Runtime;
+ move-result-object v0
+ invoke-virtual {v0}, Ljava/lang/Runtime;->gc()V
+
+ return-void
+.end method
diff --git a/test/955-lambda-smali/smali/BoxUnbox.smali b/test/955-lambda-smali/smali/BoxUnbox.smali
index 915de2d..157adb3 100644
--- a/test/955-lambda-smali/smali/BoxUnbox.smali
+++ b/test/955-lambda-smali/smali/BoxUnbox.smali
@@ -51,6 +51,7 @@
.registers 3
create-lambda v0, LBoxUnbox;->doHelloWorld(J)V
+ const-string v2, "Ljava/lang/Runnable;"
box-lambda v2, v0 # v2 = box(v0)
unbox-lambda v0, v2, J # v0 = unbox(v2)
invoke-lambda v0, {}
@@ -63,7 +64,9 @@
.registers 6 # 0 parameters, 6 locals
create-lambda v0, LBoxUnbox;->doHelloWorld(J)V
+ const-string v2, "Ljava/lang/Runnable;"
box-lambda v2, v0 # v2 = box(v0)
+ const-string v3, "Ljava/lang/Runnable;"
box-lambda v3, v0 # v3 = box(v0)
# The objects should be not-null, and they should have the same reference
@@ -116,6 +119,7 @@
const v0, 0 # v0 = null
const v1, 0 # v1 = null
:start
+ const-string v2, "Ljava/lang/Runnable;"
box-lambda v2, v0 # attempting to box a null lambda will throw NPE
:end
return-void
diff --git a/test/955-lambda-smali/smali/CaptureVariables.smali b/test/955-lambda-smali/smali/CaptureVariables.smali
index f18b7ff..531c259 100644
--- a/test/955-lambda-smali/smali/CaptureVariables.smali
+++ b/test/955-lambda-smali/smali/CaptureVariables.smali
@@ -243,6 +243,8 @@
# TODO: create-lambda should not write to both v0 and v1
invoke-lambda v0, {}
+ return-void
+
.end method
#TODO: should use a closure type instead of a long
diff --git a/test/955-lambda-smali/smali/Main.smali b/test/955-lambda-smali/smali/Main.smali
index 9892d61..e8ab84c 100644
--- a/test/955-lambda-smali/smali/Main.smali
+++ b/test/955-lambda-smali/smali/Main.smali
@@ -25,6 +25,7 @@
invoke-static {}, LBoxUnbox;->run()V
invoke-static {}, LMoveResult;->run()V
invoke-static {}, LCaptureVariables;->run()V
+ invoke-static {}, LBoxInvoke;->run()V
# TODO: add tests when verification fails
diff --git a/test/LambdaInterfaces/LambdaInterfaces.java b/test/LambdaInterfaces/LambdaInterfaces.java
new file mode 100644
index 0000000..261163d
--- /dev/null
+++ b/test/LambdaInterfaces/LambdaInterfaces.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+class LambdaInterfaces {
+ interface I {
+ public int i();
+ }
+ interface J {
+ public String foo = "foo";
+ public void j1();
+ }
+ interface K extends J {
+ }
+ interface L {
+ public int sum(int a, int b);
+ }
+ interface C {
+ public String concat(String a, String b);
+ }
+}