From 9823e788ed3368ecf826c44bfa16ccabfcbe32f8 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Wed, 3 Aug 2016 12:46:58 +0100 Subject: [PATCH] Basic implementation of invoke / invoke-polymorphic. Basic switch interpreter support for invoke-polymorphic. This change allows for virtual/interface and static invokes on method handles. Support for direct invokes (including constructors) and field getters and setters will be added in follow up changes. Bug: 30550796 Test: make test-art-host Change-Id: Ieb3a991d974060d930d56467908d5c7c11d0e38e --- runtime/dex_instruction_list.h | 7 +- runtime/interpreter/interpreter_common.cc | 146 ++++++++++++++++++++++++- runtime/interpreter/interpreter_common.h | 8 +- runtime/interpreter/interpreter_switch_impl.cc | 17 ++- runtime/method_handles.h | 51 +++++++++ runtime/mirror/method_handle_impl.h | 8 ++ runtime/verifier/method_verifier.cc | 9 +- test/955-methodhandles-smali/build | 25 +++++ test/955-methodhandles-smali/expected.txt | 2 + test/955-methodhandles-smali/info.txt | 3 + test/955-methodhandles-smali/run | 20 ++++ test/955-methodhandles-smali/smali/Main.smali | 117 ++++++++++++++++++++ test/etc/default-build | 3 + 13 files changed, 406 insertions(+), 10 deletions(-) create mode 100644 runtime/method_handles.h create mode 100755 test/955-methodhandles-smali/build create mode 100644 test/955-methodhandles-smali/expected.txt create mode 100644 test/955-methodhandles-smali/info.txt create mode 100755 test/955-methodhandles-smali/run create mode 100644 test/955-methodhandles-smali/smali/Main.smali diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h index e9749324c..3194c1a86 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex_instruction_list.h @@ -269,9 +269,8 @@ V(0xF7, UNUSED_F7, "unused-f7", k10x, kIndexUnknown, 0, kVerifyError) \ V(0xF8, UNUSED_F8, "unused-f8", k10x, kIndexUnknown, 0, kVerifyError) \ V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \ - /* TODO(narayan): The following two entries are placeholders. */ \ - V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexUnknown, 0, kVerifyError) \ - V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexUnknown, 0, kVerifyError) \ + V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero | kExperimental) \ + V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kExperimental) \ V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \ V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \ V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \ @@ -301,6 +300,8 @@ V(k31c) \ V(k35c) \ V(k3rc) \ + V(k45cc) \ + V(k4rcc) \ V(k51l) #endif // ART_RUNTIME_DEX_INSTRUCTION_LIST_H_ diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 7a6162cd0..2d90734be 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -22,7 +22,13 @@ #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "jit/jit.h" +#include "jvalue.h" +#include "method_handles.h" #include "mirror/array-inl.h" +#include "mirror/class.h" +#include "mirror/method_handle_impl.h" +#include "reflection.h" +#include "reflection-inl.h" #include "stack.h" #include "unstarted_runtime.h" #include "verifier/method_verifier.h" @@ -503,8 +509,7 @@ void AbortTransactionV(Thread* self, const char* fmt, va_list args) { } // Separate declaration is required solely for the attributes. -template +template REQUIRES_SHARED(Locks::mutator_lock_) static inline bool DoCallCommon(ArtMethod* called_method, Thread* self, @@ -576,6 +581,130 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, } } +template + REQUIRES_SHARED(Locks::mutator_lock_) +inline bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, + JValue* result) { + // Invoke-polymorphic instructions always take a receiver. i.e, they are never static. + const uint32_t vRegC = (is_range) ? inst->VRegC_4rcc() : inst->VRegC_45cc(); + + // The method_idx here is the name of the signature polymorphic method that + // was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact) + // and not the method that we'll dispatch to in the end. + // + // TODO(narayan) We'll have to check in the verifier that this is in fact a + // signature polymorphic method so that we disallow calls via invoke-polymorphic + // to non sig-poly methods. This would also have the side effect of verifying + // that vRegC really is a reference type. + mirror::MethodHandleImpl* const method_handle = + reinterpret_cast(shadow_frame.GetVRegReference(vRegC)); + if (UNLIKELY(method_handle == nullptr)) { + const int method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc(); + // Note that the invoke type is kVirtual here because a call to a signature + // polymorphic method is shaped like a virtual call at the bytecode level. + ThrowNullPointerExceptionForMethodAccess(method_idx, InvokeType::kVirtual); + + result->SetJ(0); + return false; + } + + // The vRegH value gives the index of the proto_id associated with this + // signature polymorphic callsite. + const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + + // Call through to the classlinker and ask it to resolve the static type associated + // with the callsite. This information is stored in the dex cache so it's + // guaranteed to be fast after the first resolution. + StackHandleScope<2> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* caller_class = shadow_frame.GetMethod()->GetDeclaringClass(); + mirror::MethodType* callsite_type = class_linker->ResolveMethodType( + caller_class->GetDexFile(), callsite_proto_id, + hs.NewHandle(caller_class->GetDexCache()), + hs.NewHandle(caller_class->GetClassLoader())); + + // This implies we couldn't resolve one or more types in this method handle. + if (UNLIKELY(callsite_type == nullptr)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } + + const char* old_cause = self->StartAssertNoThreadSuspension("DoInvokePolymorphic"); + + // Get the method we're actually invoking along with the kind of + // invoke that is desired. We don't need to perform access checks at this + // point because they would have been performed on our behalf at the point + // of creation of the method handle. + ArtMethod* called_method = method_handle->GetTargetMethod(); + const MethodHandleKind handle_kind = method_handle->GetHandleKind(); + mirror::MethodType* const handle_type = method_handle->GetMethodType(); + CHECK(called_method != nullptr); + CHECK(handle_type != nullptr); + + // We now have to massage the number of inputs to the target function. + // It's always one less than the number of inputs to the signature polymorphic + // invoke, the first input being a reference to the MethodHandle itself. + const uint16_t number_of_inputs = + ((is_range) ? inst->VRegA_4rcc(inst_data) : inst->VRegA_45cc(inst_data)) - 1; + + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + uint32_t receiver_vregC = 0; + if (is_range) { + receiver_vregC = (inst->VRegC_4rcc() + 1); + } else { + inst->GetVarArgs(arg, inst_data); + arg[0] = arg[1]; + arg[1] = arg[2]; + arg[2] = arg[3]; + arg[3] = arg[4]; + arg[4] = 0; + receiver_vregC = arg[0]; + } + + if (IsInvoke(handle_kind)) { + if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) { + mirror::Object* receiver = shadow_frame.GetVRegReference(receiver_vregC); + mirror::Class* declaring_class = called_method->GetDeclaringClass(); + // Verify that _vRegC is an object reference and of the type expected by + // the receiver. + called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface( + called_method, kRuntimePointerSize); + if (!VerifyObjectIsClass(receiver, declaring_class)) { + self->EndAssertNoThreadSuspension(old_cause); + return false; + } + } else if (handle_kind == kInvokeDirect) { + // TODO(narayan) : We need to handle the case where the target method is a + // constructor here. Also the case where we don't want to dynamically + // dispatch based on the type of the receiver. + self->EndAssertNoThreadSuspension(old_cause); + UNIMPLEMENTED(FATAL) << "Direct invokes are not implemented yet."; + return false; + } + + // NOTE: handle_kind == kInvokeStatic needs no special treatment here. We + // can directly make the call. handle_kind == kInvokeSuper doesn't have any + // particular use and can probably be dropped. + if (callsite_type->IsExactMatch(handle_type)) { + self->EndAssertNoThreadSuspension(old_cause); + return DoCallCommon( + called_method, self, shadow_frame, result, number_of_inputs, + arg, receiver_vregC); + } + + self->EndAssertNoThreadSuspension(old_cause); + UNIMPLEMENTED(FATAL) << "Non exact invokes are not implemented yet."; + return false; + } else { + // TODO(narayan): Implement field getters and setters. + self->EndAssertNoThreadSuspension(old_cause); + UNIMPLEMENTED(FATAL) << "Field references in method handles are not implemented yet."; + return false; + } +} + template static inline bool DoCallCommon(ArtMethod* called_method, @@ -926,6 +1055,19 @@ EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false); EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true); #undef EXPLICIT_DO_CALL_TEMPLATE_DECL +// Explicit DoInvokePolymorphic template function declarations. +#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \ + template REQUIRES_SHARED(Locks::mutator_lock_) \ + bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \ + Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \ + uint16_t inst_data, JValue* result) + +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true); +#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL + // Explicit DoFilledNewArray template function declarations. #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \ template REQUIRES_SHARED(Locks::mutator_lock_) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 0feb013e5..6b281107e 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -123,7 +123,7 @@ template bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result); -// Handles invoke-XXX/range instructions. +// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range]. // Returns true on success, otherwise throws an exception and returns false. template static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -164,6 +164,12 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr } } +// Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range). +template +bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, + JValue* result); + // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions. // Returns true on success, otherwise throws an exception and returns false. template diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 227130e7d..6cff1da35 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -1542,6 +1542,21 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } + case Instruction::INVOKE_POLYMORPHIC: { + PREAMBLE(); + bool success = DoInvokePolymorphic( + self, shadow_frame, inst, inst_data, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx); + break; + } + case Instruction::INVOKE_POLYMORPHIC_RANGE: { + PREAMBLE(); + bool success = DoInvokePolymorphic( + self, shadow_frame, inst, inst_data, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx); + break; + break; + } case Instruction::NEG_INT: PREAMBLE(); shadow_frame.SetVReg( @@ -2323,8 +2338,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::UNUSED_FC ... Instruction::UNUSED_FF: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: - case Instruction::INVOKE_POLYMORPHIC: - case Instruction::INVOKE_POLYMORPHIC_RANGE: UnexpectedOpcode(inst, shadow_frame); } } while (!interpret_one_instruction); diff --git a/runtime/method_handles.h b/runtime/method_handles.h new file mode 100644 index 000000000..5c68a8f1a --- /dev/null +++ b/runtime/method_handles.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 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_METHOD_HANDLES_H_ +#define ART_RUNTIME_METHOD_HANDLES_H_ + +#include + +namespace art { + +// Defines the behaviour of a given method handle. The behaviour +// of a handle of a given kind is identical to the dex bytecode behaviour +// of the equivalent instruction. +// +// NOTE: These must be kept in sync with the constants defined in +// java.lang.invoke.MethodHandle. +enum MethodHandleKind { + kInvokeVirtual = 0, + kInvokeSuper, + kInvokeDirect, + kInvokeStatic, + kInvokeInterface, + kInstanceGet, + kInstancePut, + kStaticGet, + kStaticPut, + kLastValidKind = kStaticPut, + kLastInvokeKind = kInvokeInterface +}; + +// Whether the given method handle kind is some variant of an invoke. +inline bool IsInvoke(const MethodHandleKind handle_kind) { + return handle_kind <= kLastInvokeKind; +} + +} // namespace art + +#endif // ART_RUNTIME_METHOD_HANDLES_H_ diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index a0aae3cdb..40716ad7c 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -20,6 +20,7 @@ #include "class.h" #include "gc_root.h" #include "object.h" +#include "method_handles.h" #include "method_type.h" namespace art { @@ -40,6 +41,13 @@ class MANAGED MethodHandle : public Object { GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); } + MethodHandleKind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) { + const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_)); + + DCHECK(handle_kind >= 0 && handle_kind <= MethodHandleKind::kLastValidKind); + return static_cast(handle_kind); + } + private: HeapReference as_type_cache_; HeapReference method_type_; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 50466edc1..ae82d6c08 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3040,6 +3040,13 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { just_set_result = true; break; } + case Instruction::INVOKE_POLYMORPHIC: + case Instruction::INVOKE_POLYMORPHIC_RANGE: { + Fail(VERIFY_ERROR_FORCE_INTERPRETER) + << "instruction is not supported by verifier; skipping verification"; + have_pending_experimental_failure_ = true; + return false; + } case Instruction::NEG_INT: case Instruction::NOT_INT: work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer()); @@ -3352,8 +3359,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::UNUSED_FC ... Instruction::UNUSED_FF: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: - case Instruction::INVOKE_POLYMORPHIC: - case Instruction::INVOKE_POLYMORPHIC_RANGE: Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); break; diff --git a/test/955-methodhandles-smali/build b/test/955-methodhandles-smali/build new file mode 100755 index 000000000..a423ca6b4 --- /dev/null +++ b/test/955-methodhandles-smali/build @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright 2016 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. + +# make us exit on a failure +set -e + +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm. + export USE_JACK=true +fi + +./default-build "$@" --experimental method-handles diff --git a/test/955-methodhandles-smali/expected.txt b/test/955-methodhandles-smali/expected.txt new file mode 100644 index 000000000..07d242216 --- /dev/null +++ b/test/955-methodhandles-smali/expected.txt @@ -0,0 +1,2 @@ +[String1]+[String2] +[String1] diff --git a/test/955-methodhandles-smali/info.txt b/test/955-methodhandles-smali/info.txt new file mode 100644 index 000000000..d10d3ebd2 --- /dev/null +++ b/test/955-methodhandles-smali/info.txt @@ -0,0 +1,3 @@ +Smali-based tests for method handle invocations. + +NOTE: needs to run under ART or a Java 8 Language runtime and compiler. diff --git a/test/955-methodhandles-smali/run b/test/955-methodhandles-smali/run new file mode 100755 index 000000000..a9f182288 --- /dev/null +++ b/test/955-methodhandles-smali/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 2016 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. + +# make us exit on a failure +set -e + +./default-run "$@" --experimental method-handles diff --git a/test/955-methodhandles-smali/smali/Main.smali b/test/955-methodhandles-smali/smali/Main.smali new file mode 100644 index 000000000..2fc92f8a8 --- /dev/null +++ b/test/955-methodhandles-smali/smali/Main.smali @@ -0,0 +1,117 @@ +# Copyright 2016 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 LMain; +.super Ljava/lang/Object; + +# MethodHandle Main.getHandleForVirtual(Class defc, String name, MethodType type); +# +# Returns a handle to a virtual method on |defc| named name with type |type| using +# the public lookup object. +.method public static getHandleForVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; +.registers 5 + + # Get a reference to the public lookup object (MethodHandles.publicLookup()). + invoke-static {}, Ljava/lang/invoke/MethodHandles;->publicLookup()Ljava/lang/invoke/MethodHandles$Lookup; + move-result-object v0 + + # Call Lookup.findVirtual(defc, name, type); + invoke-virtual {v0, p0, p1, p2}, Ljava/lang/invoke/MethodHandles$Lookup;->findVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; + move-result-object v1 + return-object v1 +.end method + +# MethodHandle Main.getHandleForStatic(Class defc, String name, MethodType type); +# +# Returns a handle to a static method on |defc| named name with type |type| using +# the public lookup object. +.method public static getHandleForStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; +.registers 5 + + # Get a reference to the public lookup object (MethodHandles.publicLookup()). + invoke-static {}, Ljava/lang/invoke/MethodHandles;->publicLookup()Ljava/lang/invoke/MethodHandles$Lookup; + move-result-object v0 + + # Call Lookup.findStatic(defc, name, type); + invoke-virtual {v0, p0, p1, p2}, Ljava/lang/invoke/MethodHandles$Lookup;->findStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; + move-result-object v1 + return-object v1 +.end method + +# Returns a method handle to String java.lang.String.concat(String); +.method public static getStringConcatHandle()Ljava/lang/invoke/MethodHandle; +.registers 3 + const-string v0, "concat" + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + move-result-object v1 + + # Call MethodType.methodType(rtype=String.class, ptype[0] = String.class) + invoke-static {v1, v1}, Ljava/lang/invoke/MethodType;->methodType(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType; + move-result-object v2 + + # Call Main.getHandleForVirtual(String.class, "concat", methodType); + invoke-static {v1, v0, v2}, LMain;->getHandleForVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; + move-result-object v0 + return-object v0 +.end method + +# Returns a method handle to static String java.lang.String.valueOf(String); +.method public static getStringValueOfHandle()Ljava/lang/invoke/MethodHandle; +.registers 4 + # set v0 to java.lang.Object.class + new-instance v0, Ljava/lang/Object; + invoke-direct {v0}, Ljava/lang/Object;->()V + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + move-result-object v0 + + # set v1 to the name of the method ("valueOf") and v2 to java.lang.String.class; + const-string v1, "valueOf" + invoke-virtual {v1}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + move-result-object v2 + + # Call MethodType.methodType(rtype=String.class, ptype[0]=Object.class) + invoke-static {v2, v0}, Ljava/lang/invoke/MethodType;->methodType(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType; + move-result-object v3 + + # Call Main.getHandleForStatic(String.class, "valueOf", methodType); + invoke-static {v2, v1, v3}, LMain;->getHandleForStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; + move-result-object v0 + return-object v0 +.end method + + +.method public static main([Ljava/lang/String;)V +.registers 5 + + # Test case 1: Exercise String.concat(String, String) which is a virtual method. + invoke-static {}, LMain;->getStringConcatHandle()Ljava/lang/invoke/MethodHandle; + move-result-object v0 + const-string v1, "[String1]" + const-string v2, "+[String2]" + invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v3 + sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + # Test case 2: Exercise String.valueOf(Object); + invoke-static {}, LMain;->getStringValueOfHandle()Ljava/lang/invoke/MethodHandle; + move-result-object v0 + const-string v1, "[String1]" + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Object;)Ljava/lang/String; + move-result-object v3 + sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method diff --git a/test/etc/default-build b/test/etc/default-build index 37ce0f216..e6634967d 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -71,13 +71,16 @@ DEFAULT_EXPERIMENT="no-experiment" declare -A JACK_EXPERIMENTAL_ARGS JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" +JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=26" declare -A SMALI_EXPERIMENTAL_ARGS SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24" +SMALI_EXPERIMENTAL_ARGS["method-handles"]="--api-level 26" declare -A JAVAC_EXPERIMENTAL_ARGS JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8" +JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.7 -target 1.7" while true; do -- 2.11.0