diff options
31 files changed, 492 insertions, 334 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 388ff061a9..6ad4eec257 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -184,6 +184,7 @@ ART_GTEST_unstarted_runtime_test_DEX_DEPS := Nested ART_GTEST_heap_verification_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler +ART_GTEST_oatdump_app_test_DEX_DEPS := ProfileTestMultiDex # The elf writer test has dependencies on core.oat. ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32) @@ -303,6 +304,11 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ oatdumpd-target ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) +ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \ + dex2oatd-host \ + dex2oatds-host +ART_GTEST_oatdump_app_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) \ + dex2oatd-target ART_GTEST_patchoat_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) @@ -507,13 +513,19 @@ define define-art-gtest-rule-host $$(gtest_exe) \ $$(ART_GTEST_$(1)_HOST_DEPS) \ $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) + ifneq (,$(DIST_DIR)) + gtest_xml_output := --gtest_output=xml:$(DIST_DIR)/gtest/$(1)$$($(3)ART_PHONY_TEST_HOST_SUFFIX).xml + else + gtest_xml_output := + endif ART_TEST_HOST_GTEST_DEPENDENCIES += $$(gtest_deps) .PHONY: $$(gtest_rule) ifeq (,$(SANITIZE_HOST)) +$$(gtest_rule): PRIVATE_XML_OUTPUT := $$(gtest_xml_output) $$(gtest_rule): $$(gtest_exe) $$(gtest_deps) - $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && \ + $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< $$(PRIVATE_XML_OUTPUT) && \ $$(call ART_TEST_PASSED,$$@)) || $$(call ART_TEST_FAILED,$$@) else # Note: envsetup currently exports ASAN_OPTIONS=detect_leaks=0 to suppress leak detection, as some @@ -523,9 +535,10 @@ else # (with the x86-64 ABI, as this allows symbolization of both x86 and x86-64). We don't do this in # general as it loses all the color output, and we have our own symbolization step when not running # under ASAN. +$$(gtest_rule): PRIVATE_XML_OUTPUT := $$(gtest_xml_output) $$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(hide) ($$(call ART_TEST_SKIP,$$@) && set -o pipefail && \ - ASAN_OPTIONS=detect_leaks=1 $$< 2>&1 | tee $$<.tmp.out >&2 && \ + ASAN_OPTIONS=detect_leaks=1 $$< $$(PRIVATE_XML_OUTPUT) 2>&1 | tee $$<.tmp.out >&2 && \ { $$(call ART_TEST_PASSED,$$@) ; rm $$<.tmp.out ; }) || \ ( grep -q AddressSanitizer $$<.tmp.out && export ANDROID_BUILD_TOP=`pwd` && \ { echo "ABI: 'x86_64'" | cat - $$<.tmp.out | development/scripts/stack | tail -n 3000 ; } ; \ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8a604db59e..bd3a145368 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -936,13 +936,13 @@ void CompilerDriver::PreCompile(jobject class_loader, // instances of this thread's stack. LOG(FATAL_WITHOUT_ABORT) << "Had a hard failure verifying all classes, and was asked to abort " << "in such situations. Please check the log."; - abort(); + _exit(1); } else if (number_of_soft_verifier_failures_ > 0 && GetCompilerOptions().AbortOnSoftVerifierFailure()) { LOG(FATAL_WITHOUT_ABORT) << "Had " << number_of_soft_verifier_failures_ << " soft failure(s) " << "verifying all classes, and was asked to abort in such situations. " << "Please check the log."; - abort(); + _exit(1); } if (compiler_options_->IsAnyCompilationEnabled()) { diff --git a/oatdump/Android.bp b/oatdump/Android.bp index 012100d470..71e276db10 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -121,6 +121,7 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ + "oatdump_app_test.cc", "oatdump_test.cc", "oatdump_image_test.cc", ], diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index f91f6e3b74..41133a825e 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1676,10 +1676,10 @@ class OatDumper { if ((method_access_flags & kAccNative) == 0) { ScopedObjectAccess soa(Thread::Current()); Runtime* const runtime = Runtime::Current(); - Handle<mirror::DexCache> dex_cache( - hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file, nullptr))); - CHECK(dex_cache != nullptr); DCHECK(options_.class_loader_ != nullptr); + Handle<mirror::DexCache> dex_cache = hs->NewHandle( + runtime->GetClassLinker()->RegisterDexFile(*dex_file, options_.class_loader_->Get())); + CHECK(dex_cache != nullptr); return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, class_def, code_item, nullptr, method_access_flags); @@ -2998,7 +2998,7 @@ static jobject InstallOatFile(Runtime* runtime, // Need well-known-classes. WellKnownClasses::Init(self->GetJniEnv()); - // Need to register dex files to get a working dex cache. + // Open dex files. OatFile* oat_file_ptr = oat_file.get(); ClassLinker* class_linker = runtime->GetClassLinker(); runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file)); @@ -3006,9 +3006,6 @@ static jobject InstallOatFile(Runtime* runtime, std::string error_msg; const DexFile* const dex_file = OpenDexFile(odf, &error_msg); CHECK(dex_file != nullptr) << error_msg; - ObjPtr<mirror::DexCache> dex_cache = - class_linker->RegisterDexFile(*dex_file, nullptr); - CHECK(dex_cache != nullptr); class_path->push_back(dex_file); } @@ -3019,6 +3016,13 @@ static jobject InstallOatFile(Runtime* runtime, jobject class_loader = class_linker->CreatePathClassLoader(self, *class_path); + // Need to register dex files to get a working dex cache. + for (const DexFile* dex_file : *class_path) { + ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile( + *dex_file, self->DecodeJObject(class_loader)->AsClassLoader()); + CHECK(dex_cache != nullptr); + } + return class_loader; } diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc new file mode 100644 index 0000000000..f12522277b --- /dev/null +++ b/oatdump/oatdump_app_test.cc @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 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 "oatdump_test.h" + +namespace art { + +TEST_F(OatDumpTest, TestAppWithBootImage) { + std::string error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; +} +TEST_F(OatDumpTest, TestAppWithBootImageStatic) { + TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); + std::string error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kStatic, {}, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; +} + +TEST_F(OatDumpTest, TestPicAppWithBootImage) { + std::string error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--compile-pic"}, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; +} +TEST_F(OatDumpTest, TestPicAppWithBootImageStatic) { + TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); + std::string error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--compile-pic"}, &error_msg)) << error_msg; + ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; +} + +} // namespace art diff --git a/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc index e9cc922d9b..d054ecefbb 100644 --- a/oatdump/oatdump_image_test.cc +++ b/oatdump/oatdump_image_test.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 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. diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index d0f05d9e66..d4bed6b3da 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -66,14 +66,15 @@ class OatDumpTest : public CommonRuntimeTest { // Linking flavor. enum Flavor { - kDynamic, // oatdump(d) - kStatic, // oatdump(d)s + kDynamic, // oatdump(d), dex2oat(d) + kStatic, // oatdump(d)s, dex2oat(d)s }; - // Returns path to the oatdump binary. - std::string GetOatDumpFilePath(Flavor flavor) { + // Returns path to the oatdump/dex2oat binary. + std::string GetExecutableFilePath(Flavor flavor, const char* name) { std::string root = GetTestAndroidRoot(); - root += "/bin/oatdump"; + root += "/bin/"; + root += name; if (kIsDebugBuild) { root += "d"; } @@ -85,6 +86,7 @@ class OatDumpTest : public CommonRuntimeTest { enum Mode { kModeOat, + kModeOatWithBootImage, kModeArt, kModeSymbolize, }; @@ -95,13 +97,56 @@ class OatDumpTest : public CommonRuntimeTest { kListAndCode }; + std::string GetAppBaseName() { + // Use ProfileTestMultiDex as it contains references to boot image strings + // that shall use different code for PIC and non-PIC. + return "ProfileTestMultiDex"; + } + + std::string GetAppOdexName() { + return tmp_dir_ + "/" + GetAppBaseName() + ".odex"; + } + + bool GenerateAppOdexFile(Flavor flavor, + const std::vector<std::string>& args, + /*out*/ std::string* error_msg) { + std::string dex2oat_path = GetExecutableFilePath(flavor, "dex2oat"); + std::vector<std::string> exec_argv = { + dex2oat_path, + "--runtime-arg", + "-Xms64m", + "--runtime-arg", + "-Xmx512m", + "--runtime-arg", + "-Xnorelocate", + "--boot-image=" + GetCoreArtLocation(), + "--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)), + "--dex-file=" + GetTestDexFileName(GetAppBaseName().c_str()), + "--oat-file=" + GetAppOdexName(), + "--compiler-filter=speed" + }; + exec_argv.insert(exec_argv.end(), args.begin(), args.end()); + + pid_t pid; + int pipe_fd; + bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); + if (result) { + close(pipe_fd); + int status = 0; + if (waitpid(pid, &status, 0) != -1) { + result = (status == 0); + } + } + return result; + } + // Run the test with custom arguments. bool Exec(Flavor flavor, Mode mode, const std::vector<std::string>& args, Display display, - std::string* error_msg) { - std::string file_path = GetOatDumpFilePath(flavor); + /*out*/ std::string* error_msg) { + std::string file_path = GetExecutableFilePath(flavor, "oatdump"); EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; @@ -133,6 +178,11 @@ class OatDumpTest : public CommonRuntimeTest { expected_prefixes.push_back("IMAGE LOCATION:"); expected_prefixes.push_back("IMAGE BEGIN:"); expected_prefixes.push_back("kDexCaches:"); + } else if (mode == kModeOatWithBootImage) { + exec_argv.push_back("--boot-image=" + GetCoreArtLocation()); + exec_argv.push_back("--instruction-set=" + std::string( + GetInstructionSetString(kRuntimeISA))); + exec_argv.push_back("--oat-file=" + GetAppOdexName()); } else { CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat)); exec_argv.push_back("--oat-file=" + core_oat_location_); @@ -140,39 +190,10 @@ class OatDumpTest : public CommonRuntimeTest { } exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - bool result = true; - // We must set --android-root. - int link[2]; - if (pipe(link) == -1) { - *error_msg = strerror(errno); - return false; - } - - const pid_t pid = fork(); - if (pid == -1) { - *error_msg = strerror(errno); - return false; - } - - if (pid == 0) { - dup2(link[1], STDOUT_FILENO); - close(link[0]); - close(link[1]); - // change process groups, so we don't get reaped by ProcessManager - setpgid(0, 0); - // Use execv here rather than art::Exec to avoid blocking on waitpid here. - std::vector<char*> argv; - for (size_t i = 0; i < exec_argv.size(); ++i) { - argv.push_back(const_cast<char*>(exec_argv[i].c_str())); - } - argv.push_back(nullptr); - UNUSED(execv(argv[0], &argv[0])); - const std::string command_line(android::base::Join(exec_argv, ' ')); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; - // _exit to avoid atexit handlers in child. - _exit(1); - } else { - close(link[1]); + pid_t pid; + int pipe_fd; + bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); + if (result) { static const size_t kLineMax = 256; char line[kLineMax] = {}; size_t line_len = 0; @@ -188,7 +209,7 @@ class OatDumpTest : public CommonRuntimeTest { memmove(&line[0], &line[spaces], line_len); } ssize_t bytes_read = - TEMP_FAILURE_RETRY(read(link[0], &line[line_len], kLineMax - line_len)); + TEMP_FAILURE_RETRY(read(pipe_fd, &line[line_len], kLineMax - line_len)); if (bytes_read <= 0) { break; } @@ -219,7 +240,7 @@ class OatDumpTest : public CommonRuntimeTest { EXPECT_GT(total, 0u); } LOG(INFO) << "Processed bytes " << total; - close(link[0]); + close(pipe_fd); int status = 0; if (waitpid(pid, &status, 0) != -1) { result = (status == 0); @@ -236,6 +257,49 @@ class OatDumpTest : public CommonRuntimeTest { return result; } + bool ForkAndExec(const std::vector<std::string>& exec_argv, + /*out*/ pid_t* pid, + /*out*/ int* pipe_fd, + /*out*/ std::string* error_msg) { + int link[2]; + if (pipe(link) == -1) { + *error_msg = strerror(errno); + return false; + } + + *pid = fork(); + if (*pid == -1) { + *error_msg = strerror(errno); + close(link[0]); + close(link[1]); + return false; + } + + if (*pid == 0) { + dup2(link[1], STDOUT_FILENO); + close(link[0]); + close(link[1]); + // change process groups, so we don't get reaped by ProcessManager + setpgid(0, 0); + // Use execv here rather than art::Exec to avoid blocking on waitpid here. + std::vector<char*> argv; + for (size_t i = 0; i < exec_argv.size(); ++i) { + argv.push_back(const_cast<char*>(exec_argv[i].c_str())); + } + argv.push_back(nullptr); + UNUSED(execv(argv[0], &argv[0])); + const std::string command_line(android::base::Join(exec_argv, ' ')); + PLOG(ERROR) << "Failed to execv(" << command_line << ")"; + // _exit to avoid atexit handlers in child. + _exit(1); + UNREACHABLE(); + } else { + close(link[1]); + *pipe_fd = link[0]; + return true; + } + } + std::string tmp_dir_; private: diff --git a/test/952-invoke-custom-kinds/build b/test/952-invoke-custom-kinds/build deleted file mode 100644 index a02cdc3769..0000000000 --- a/test/952-invoke-custom-kinds/build +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 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. - -# Stop if something fails. -set -e - -${DX} --dex --min-sdk-version=26 --output=classes.dex classes - -zip $TEST_NAME.jar classes.dex diff --git a/test/952-invoke-custom-kinds/classes/Main.class b/test/952-invoke-custom-kinds/classes/Main.class Binary files differdeleted file mode 100644 index 6bc04e326a..0000000000 --- a/test/952-invoke-custom-kinds/classes/Main.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class Binary files differdeleted file mode 100644 index 5dfe958795..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class Binary files differdeleted file mode 100644 index a11ee696bf..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class Binary files differdeleted file mode 100644 index e233febbf4..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class Binary files differdeleted file mode 100644 index 41e1d431f2..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class Binary files differdeleted file mode 100644 index b8dcd5559a..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class Binary files differdeleted file mode 100644 index 7906f99d2d..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class Binary files differdeleted file mode 100644 index c3266e49f7..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class Binary files differdeleted file mode 100644 index 03dc23396c..0000000000 --- a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class +++ /dev/null diff --git a/test/952-invoke-custom-kinds/expected.txt b/test/952-invoke-custom-kinds/expected.txt deleted file mode 100644 index c41b5c6d88..0000000000 --- a/test/952-invoke-custom-kinds/expected.txt +++ /dev/null @@ -1,40 +0,0 @@ -bsmLookupStatic [] -Hello World! -bsmLookupStatic [] -true -127 -c -1024 -123456 -1.2 -123456789 -3.5123456789 -String -bsmLookupStaticWithExtraArgs [1, 123456789, 123.456, 123456.789123] -targetMethodTest3 from InvokeCustom -bsmCreateCallSite [MethodHandle(InvokeCustom)void] -targetMethodTest4 from Super -bsmLookupStatic [] -targetMethodTest5 1000 + -923 = 77 -targetMethodTest5 returned: 77 -bsmLookupStatic [] -targetMethodTest6 8209686820727 + -1172812402961 = 7036874417766 -targetMethodTest6 returned: 7036874417766 -bsmLookupStatic [] -targetMethodTest7 0.50097656 * -0.50097656 = -0.2509775161743164 -targetMethodTest6 returned: -0.2509775161743164 -bsmLookupStatic [] -targetMethodTest8 First invokedynamic invocation -bsmLookupStatic [] -targetMethodTest8 Second invokedynamic invocation -bsmLookupStatic [] -targetMethodTest8 Dupe first invokedynamic invocation -bsmLookupTest9 [MethodHandle()int, MethodHandle(int)void, MethodHandle(InvokeCustom)float, MethodHandle(InvokeCustom,float)void] -targetMethodTest9 ()void -checkStaticFieldTest9: old 0 new 1985229328 expected 1985229328 OK -checkFieldTest9: old 0.0 new 1.99E-19 expected 1.99E-19 OK -helperMethodTest9 in class invokecustom.InvokeCustom -InvokeCustom.<init>(3) -run() for Test9 -InvokeCustom.privateMethodTest9() -targetMethodTest9() diff --git a/test/952-invoke-custom-kinds/info.txt b/test/952-invoke-custom-kinds/info.txt deleted file mode 100644 index 33b4cffc1d..0000000000 --- a/test/952-invoke-custom-kinds/info.txt +++ /dev/null @@ -1,4 +0,0 @@ -This test checks call sites and constant method handles in DEX files used -by invoke-custom. - -The class files come from dalvik/dx/tests/135-invoke-custom. diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt index be01c453fb..767cc7e734 100644 --- a/test/952-invoke-custom/expected.txt +++ b/test/952-invoke-custom/expected.txt @@ -13,3 +13,8 @@ Linking _add (int,int)int 9000 TestLinkerUnrelatedBSM Winners 1 Votes 16 +TestInvocationKinds +testStaticFieldAccessors +testInstanceFieldAccessors +testInvokeVirtual => max(77, -3) = 77 +testConstructor => class TestInvocationKinds$Widget diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java index 2e1db8200f..0b1c1fffe5 100644 --- a/test/952-invoke-custom/src/Main.java +++ b/test/952-invoke-custom/src/Main.java @@ -86,5 +86,6 @@ public class Main extends TestBase { TestLinkerMethodMultipleArgumentTypes(); TestLinkerUnrelatedBSM.test(); TestInvokeCustomWithConcurrentThreads(); + TestInvocationKinds.test(); } } diff --git a/test/952-invoke-custom/src/TestInvocationKinds.java b/test/952-invoke-custom/src/TestInvocationKinds.java new file mode 100644 index 0000000000..7b88c18c66 --- /dev/null +++ b/test/952-invoke-custom/src/TestInvocationKinds.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2018 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. + */ + +import annotations.BootstrapMethod; +import annotations.CalledByIndy; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +class TestInvocationKinds extends TestBase { + private static int static_field; + private double instance_field; + + static CallSite lookupStaticFieldGetter( + MethodHandles.Lookup lookup, String name, MethodType methodType) throws Throwable { + // methodType = "()LfieldType;" + MethodHandle mh = + lookup.findStaticGetter(TestInvocationKinds.class, name, methodType.returnType()); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestInvocationKinds.class, + name = "lookupStaticFieldSetter" + ), + fieldOrMethodName = "static_field", + returnType = void.class, + parameterTypes = {int.class} + ) + private static void setStaticField(int value) { + assertNotReached(); + } + + static CallSite lookupStaticFieldSetter( + MethodHandles.Lookup lookup, String name, MethodType methodType) throws Throwable { + // methodType = "(LfieldType;)V" + MethodHandle mh = + lookup.findStaticSetter( + TestInvocationKinds.class, name, methodType.parameterType(0)); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestInvocationKinds.class, + name = "lookupStaticFieldGetter" + ), + fieldOrMethodName = "static_field", + returnType = int.class, + parameterTypes = {} + ) + private static int getStaticField() { + assertNotReached(); + return 0; + } + + static CallSite lookupInstanceFieldSetter( + MethodHandles.Lookup lookup, String name, MethodType methodType) throws Throwable { + // methodType = "(Lreceiver;LfieldType;)V" + MethodHandle mh = + lookup.findSetter(methodType.parameterType(0), name, methodType.parameterType(1)); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestInvocationKinds.class, + name = "lookupInstanceFieldSetter" + ), + fieldOrMethodName = "instance_field", + returnType = void.class, + parameterTypes = {TestInvocationKinds.class, double.class} + ) + private static void setInstanceField(TestInvocationKinds instance, double value) { + assertNotReached(); + instance.instance_field = Double.NaN; + } + + static CallSite lookupInstanceFieldGetter( + MethodHandles.Lookup lookup, String name, MethodType methodType) throws Throwable { + // methodType = "(Lreceiver;)LfieldType;" + MethodHandle mh = + lookup.findGetter(methodType.parameterType(0), name, methodType.returnType()); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestInvocationKinds.class, + name = "lookupInstanceFieldGetter" + ), + fieldOrMethodName = "instance_field", + returnType = double.class, + parameterTypes = {TestInvocationKinds.class} + ) + private static double getInstanceField(TestInvocationKinds instance) { + assertNotReached(); + return Double.NaN; + } + + private static void testStaticFieldAccessors() { + System.out.println("testStaticFieldAccessors"); + setStaticField(3); + assertEquals(static_field, 3); + setStaticField(4); + assertEquals(static_field, 4); + assertEquals(static_field, getStaticField()); + static_field = Integer.MAX_VALUE; + assertEquals(Integer.MAX_VALUE, getStaticField()); + } + + private static void testInstanceFieldAccessors() { + System.out.println("testInstanceFieldAccessors"); + TestInvocationKinds instance = new TestInvocationKinds(); + instance.instance_field = Double.MIN_VALUE; + setInstanceField(instance, Math.PI); + assertEquals(Math.PI, instance.instance_field); + instance.instance_field = Math.E; + assertEquals(Math.E, getInstanceField(instance)); + } + + private static CallSite lookupVirtual( + MethodHandles.Lookup lookup, String name, MethodType methodType) throws Throwable { + // To get the point-of-use and invokedynamic to work the methodType here has the + // receiver type as the leading paramter which needs to be dropped for findVirtual(). + MethodType mt = methodType.dropParameterTypes(0, 1); + MethodHandle mh = lookup.findVirtual(TestInvocationKinds.class, name, mt); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestInvocationKinds.class, name = "lookupVirtual"), + fieldOrMethodName = "getMaxIntegerValue", + returnType = int.class, + parameterTypes = {TestInvocationKinds.class, int.class, int.class} + ) + private static int maxIntegerValue(TestInvocationKinds receiver, int x, int y) { + assertNotReached(); + return 0; + } + + public int getMaxIntegerValue(int x, int y) { + return x > y ? x : y; + } + + static void testInvokeVirtual() { + System.out.print("testInvokeVirtual => max(77, -3) = "); + TestInvocationKinds receiver = new TestInvocationKinds(); + int result = maxIntegerValue(receiver, 77, -3); + System.out.println(result); + } + + static class Widget { + int value; + public Widget(int value) {} + } + + private static CallSite lookupConstructor( + MethodHandles.Lookup lookup, String name, MethodType methodType) throws Throwable { + // methodType = (constructorParams);classToBeConstructed + Class<?> cls = methodType.returnType(); + MethodType constructorMethodType = methodType.changeReturnType(void.class); + MethodHandle mh = lookup.findConstructor(cls, constructorMethodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestInvocationKinds.class, + name = "lookupConstructor" + ), + fieldOrMethodName = "unused", + returnType = Widget.class, + parameterTypes = {int.class} + ) + private static Widget makeWidget(int v) { + assertNotReached(); + return null; + } + + static void testConstructor() { + System.out.print("testConstructor => "); + Widget receiver = makeWidget(3); + assertEquals(Widget.class, receiver.getClass()); + System.out.println(receiver.getClass()); + } + + public static void test() { + System.out.println(TestInvocationKinds.class.getName()); + testStaticFieldAccessors(); + testInstanceFieldAccessors(); + testInvokeVirtual(); + testConstructor(); + } +} diff --git a/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java index 761d1820db..2ef7ff7e70 100644 --- a/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java +++ b/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java @@ -14,9 +14,8 @@ * limitations under the License. */ +import annotations.BootstrapMethod; import annotations.CalledByIndy; -import annotations.LinkerMethodHandle; -import annotations.MethodHandleKind; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; @@ -66,16 +65,15 @@ public class TestInvokeCustomWithConcurrentThreads extends TestBase implements R } @CalledByIndy( - invokeMethodHandle = - @LinkerMethodHandle( - kind = MethodHandleKind.INVOKE_STATIC, + bootstrapMethod = + @BootstrapMethod( enclosingType = TestInvokeCustomWithConcurrentThreads.class, name = "linkerMethod", - argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class} + parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class} ), - name = "setCalled", + fieldOrMethodName = "setCalled", returnType = int.class, - argumentTypes = {int.class} + parameterTypes = {int.class} ) private static int setCalled(int index) { called[index].getAndIncrement(); diff --git a/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java index 74ac3cd15e..ff598bb24f 100644 --- a/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java +++ b/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java @@ -14,9 +14,8 @@ * limitations under the License. */ +import annotations.BootstrapMethod; import annotations.CalledByIndy; -import annotations.LinkerMethodHandle; -import annotations.MethodHandleKind; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; @@ -32,16 +31,15 @@ public class TestLinkerMethodMinimalArguments extends TestBase { static final int FAILURE_TYPE_TARGET_METHOD_THROWS = 3; @CalledByIndy( - invokeMethodHandle = - @LinkerMethodHandle( - kind = MethodHandleKind.INVOKE_STATIC, + bootstrapMethod = + @BootstrapMethod( enclosingType = TestLinkerMethodMinimalArguments.class, - argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, name = "linkerMethod" ), - name = "_add", + fieldOrMethodName = "_add", returnType = int.class, - argumentTypes = {int.class, int.class} + parameterTypes = {int.class, int.class} ) private static int add(int a, int b) { assertNotReached(); diff --git a/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java index acb698675f..0015c00844 100644 --- a/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java +++ b/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java @@ -14,10 +14,9 @@ * limitations under the License. */ +import annotations.BootstrapMethod; import annotations.CalledByIndy; import annotations.Constant; -import annotations.LinkerMethodHandle; -import annotations.MethodHandleKind; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; @@ -29,12 +28,11 @@ public class TestLinkerMethodMultipleArgumentTypes extends TestBase { private static int bootstrapRunCount = 0; @CalledByIndy( - invokeMethodHandle = - @LinkerMethodHandle( - kind = MethodHandleKind.INVOKE_STATIC, + bootstrapMethod = + @BootstrapMethod( enclosingType = TestLinkerMethodMultipleArgumentTypes.class, name = "linkerMethod", - argumentTypes = { + parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, @@ -50,7 +48,10 @@ public class TestLinkerMethodMultipleArgumentTypes extends TestBase { long.class } ), - methodHandleExtraArgs = { + fieldOrMethodName = "_add", + returnType = int.class, + parameterTypes = {int.class, int.class}, + constantArgumentsForBootstrapMethod = { @Constant(intValue = -1), @Constant(intValue = 1), @Constant(intValue = (int) 'a'), @@ -61,10 +62,7 @@ public class TestLinkerMethodMultipleArgumentTypes extends TestBase { @Constant(stringValue = "Hello"), @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class), @Constant(longValue = 123456789L) - }, - name = "_add", - returnType = int.class, - argumentTypes = {int.class, int.class} + } ) private static int add(int a, int b) { assertNotReached(); diff --git a/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java b/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java index 3a63b332b6..139a1720f4 100644 --- a/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java +++ b/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java @@ -14,20 +14,18 @@ * limitations under the License. */ +import annotations.BootstrapMethod; import annotations.CalledByIndy; import annotations.Constant; -import annotations.LinkerMethodHandle; -import annotations.MethodHandleKind; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; class TestLinkerUnrelatedBSM extends TestBase { @CalledByIndy( - invokeMethodHandle = - @LinkerMethodHandle( - kind = MethodHandleKind.INVOKE_STATIC, + bootstrapMethod = + @BootstrapMethod( enclosingType = UnrelatedBSM.class, - argumentTypes = { + parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, @@ -35,10 +33,10 @@ class TestLinkerUnrelatedBSM extends TestBase { }, name = "bsm" ), - methodHandleExtraArgs = {@Constant(classValue = TestLinkerUnrelatedBSM.class)}, - name = "_addf", + fieldOrMethodName = "_addf", returnType = float.class, - argumentTypes = {float.class, float.class} + parameterTypes = {float.class, float.class}, + constantArgumentsForBootstrapMethod = {@Constant(classValue = TestLinkerUnrelatedBSM.class)} ) private static float addf(float a, float b) { assertNotReached(); @@ -50,11 +48,10 @@ class TestLinkerUnrelatedBSM extends TestBase { } @CalledByIndy( - invokeMethodHandle = - @LinkerMethodHandle( - kind = MethodHandleKind.INVOKE_STATIC, + bootstrapMethod = + @BootstrapMethod( enclosingType = UnrelatedBSM.class, - argumentTypes = { + parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, @@ -62,10 +59,10 @@ class TestLinkerUnrelatedBSM extends TestBase { }, name = "bsm" ), - methodHandleExtraArgs = {@Constant(classValue = TestLinkerUnrelatedBSM.class)}, - name = "_subf", + fieldOrMethodName = "_subf", returnType = float.class, - argumentTypes = {float.class, float.class} + parameterTypes = {float.class, float.class}, + constantArgumentsForBootstrapMethod = {@Constant(classValue = TestLinkerUnrelatedBSM.class)} ) private static float subf(float a, float b) { assertNotReached(); diff --git a/test/952-invoke-custom/src/annotations/LinkerMethodHandle.java b/test/952-invoke-custom/src/annotations/BootstrapMethod.java index e0e56c578d..c16783007f 100644 --- a/test/952-invoke-custom/src/annotations/LinkerMethodHandle.java +++ b/test/952-invoke-custom/src/annotations/BootstrapMethod.java @@ -21,18 +21,25 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; -/** Describe a linker method to a method. */ +/** + * Describes a bootstrap method that performs method handle resolution on behalf of an + * invoke-dynamic instruction. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) -public @interface LinkerMethodHandle { - MethodHandleKind kind(); - +public @interface BootstrapMethod { + /** The class containing the bootstrap method. */ Class<?> enclosingType(); + /** The bootstrap method name. */ String name(); + /** The return type of the bootstrap method. */ Class<?> returnType() default CallSite.class; - Class<?>[] argumentTypes() default {}; + /** The parameter types of the bootstrap method. */ + Class<?>[] parameterTypes() default {Lookup.class, String.class, MethodType.class}; } diff --git a/test/952-invoke-custom/src/annotations/CalledByIndy.java b/test/952-invoke-custom/src/annotations/CalledByIndy.java index 17b8259af4..c4d13a2af4 100644 --- a/test/952-invoke-custom/src/annotations/CalledByIndy.java +++ b/test/952-invoke-custom/src/annotations/CalledByIndy.java @@ -28,15 +28,17 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CalledByIndy { - LinkerMethodHandle[] invokeMethodHandle() default {}; + /** Resolver metadata for bootstrapping */ + BootstrapMethod[] bootstrapMethod() default {}; - LinkerFieldHandle[] fieldMethodHandle() default {}; - - String name(); + /** Field or method name. */ + String fieldOrMethodName(); + /** Return type of method() or field getter() */ Class<?> returnType() default void.class; - Class<?>[] argumentTypes() default {}; + /** Types of parameters for method or field setter() */ + Class<?>[] parameterTypes() default {}; - Constant[] methodHandleExtraArgs() default {}; + Constant[] constantArgumentsForBootstrapMethod() default {}; } diff --git a/test/952-invoke-custom/src/annotations/LinkerFieldHandle.java b/test/952-invoke-custom/src/annotations/LinkerFieldHandle.java deleted file mode 100644 index a3efe24107..0000000000 --- a/test/952-invoke-custom/src/annotations/LinkerFieldHandle.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -package annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.invoke.CallSite; - -/** Describe a linker method to a field. */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -public @interface LinkerFieldHandle { - MethodHandleKind kind(); - - Class<?> enclosingType(); - - String name(); - - Class<?> type() default CallSite.class; -} diff --git a/test/952-invoke-custom/src/annotations/MethodHandleKind.java b/test/952-invoke-custom/src/annotations/MethodHandleKind.java deleted file mode 100644 index 5847e2f39f..0000000000 --- a/test/952-invoke-custom/src/annotations/MethodHandleKind.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -package annotations; - -/** MethodHandle invocations kinds supported by invokedynamic */ -public enum MethodHandleKind { - GET_FIELD, - GET_STATIC, - PUT_FIELD, - PUT_STATIC, - INVOKE_VIRTUAL, - INVOKE_STATIC, - INVOKE_SPECIAL, - INVOKE_CONSTRUCTOR, - INVOKE_INTERFACE -} diff --git a/test/952-invoke-custom/src/transformer/IndyTransformer.java b/test/952-invoke-custom/src/transformer/IndyTransformer.java index 286c09859d..45cb4760c9 100644 --- a/test/952-invoke-custom/src/transformer/IndyTransformer.java +++ b/test/952-invoke-custom/src/transformer/IndyTransformer.java @@ -15,11 +15,9 @@ */ package transformer; +import annotations.BootstrapMethod; import annotations.CalledByIndy; import annotations.Constant; -import annotations.LinkerFieldHandle; -import annotations.LinkerMethodHandle; -import annotations.MethodHandleKind; import java.io.InputStream; import java.io.OutputStream; import java.lang.invoke.MethodType; @@ -43,18 +41,17 @@ import org.objectweb.asm.Type; /** * Class for inserting invoke-dynamic instructions in annotated Java class files. * - * This class replaces static method invocations of annotated methods - * with invoke-dynamic instructions. Suppose a method is annotated as: + * <p>This class replaces static method invocations of annotated methods with invoke-dynamic + * instructions. Suppose a method is annotated as: <code> * * @CalledByIndy( - * invokeMethodHandle = - * @LinkerMethodHandle( - * kind = MethodHandleKind.INVOKE_STATIC, - * enclosingType = TestLinkerMethodMinimalArguments.class, - * argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, - * name = "linkerMethod" - * ), - * name = "magicAdd", + * bootstrapMethod = + * @BootstrapMethod( + * enclosingType = TestLinkerMethodMinimalArguments.class, + * parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + * name = "linkerMethod" + * ), + * fieldOdMethodName = "magicAdd", * returnType = int.class, * argumentTypes = {int.class, int.class} * ) @@ -66,13 +63,11 @@ import org.objectweb.asm.Type; * return x + y; * } * - * Then invokestatic bytecodes targeting the add() method will be - * replaced invokedynamic instructions targetting the CallSite that is - * construction by the bootstrap method described by the @CalledByIndy - * annotation. + * </code> Then invokestatic bytecodes targeting the add() method will be replaced invokedynamic + * instructions targetting the CallSite that is construction by the bootstrap method described by + * the @CalledByIndy annotation. * - * In the example above, this results in add() being replaced by - * invocations of magicAdd(). + * <p>In the example above, this results in add() being replaced by invocations of magicAdd(). */ class IndyTransformer { @@ -101,7 +96,7 @@ class IndyTransformer { if (opcode == org.objectweb.asm.Opcodes.INVOKESTATIC) { CalledByIndy callsite = callsiteMap.get(name); if (callsite != null) { - insertIndy(callsite.name(), desc, callsite); + insertIndy(callsite.fieldOrMethodName(), desc, callsite); return; } } @@ -109,80 +104,26 @@ class IndyTransformer { } private void insertIndy(String name, String desc, CalledByIndy callsite) { - Handle bsm = buildBootstrapMethodHandle(callsite); - Object[] bsmArgs = buildBootstrapArguments(callsite); + Handle bsm = buildBootstrapMethodHandle(callsite.bootstrapMethod()[0]); + Object[] bsmArgs = + buildBootstrapArguments(callsite.constantArgumentsForBootstrapMethod()); mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); } - private Handle buildBootstrapMethodHandle(CalledByIndy callsite) { - MethodHandleKind kind; - if (callsite.fieldMethodHandle().length != 0) { - return buildBootstrapMethodHandleForField(callsite.fieldMethodHandle()[0]); - } else if (callsite.invokeMethodHandle().length != 0) { - return buildBootstrapMethodHandleForMethod( - callsite.invokeMethodHandle()[0]); - } else { - throw new Error("Missing linker method handle in CalledByIndy annotation"); - } - } - - private Handle buildBootstrapMethodHandleForField(LinkerFieldHandle fieldHandle) { - int handleKind; - switch (fieldHandle.kind()) { - case GET_FIELD: - handleKind = Opcodes.H_GETFIELD; - break; - case GET_STATIC: - handleKind = Opcodes.H_GETSTATIC; - break; - case PUT_FIELD: - handleKind = Opcodes.H_PUTFIELD; - break; - case PUT_STATIC: - handleKind = Opcodes.H_PUTSTATIC; - break; - default: - throw new Error("Unknown field invocation kind: " + fieldHandle.kind()); - } - Class<?> resolverClass = fieldHandle.enclosingType(); - String resolverMethod = fieldHandle.name(); - Class<?> resolverReturnType = fieldHandle.type(); - - // TODO: arguments types to invoke resolver with (default + extra args). - throw new Error("WIP"); - } - - private Handle buildBootstrapMethodHandleForMethod( - LinkerMethodHandle methodHandle) { - int handleKind; - switch (methodHandle.kind()) { - case INVOKE_CONSTRUCTOR: - handleKind = Opcodes.H_NEWINVOKESPECIAL; - break; - case INVOKE_INTERFACE: - handleKind = Opcodes.H_INVOKEINTERFACE; - break; - case INVOKE_SPECIAL: - handleKind = Opcodes.H_INVOKESPECIAL; - break; - case INVOKE_STATIC: - handleKind = Opcodes.H_INVOKESTATIC; - break; - case INVOKE_VIRTUAL: - handleKind = Opcodes.H_INVOKEVIRTUAL; - break; - default: - throw new Error( - "Unknown method invocation kind: " + methodHandle.kind()); - } - String className = Type.getInternalName(methodHandle.enclosingType()); - String methodName = methodHandle.name(); + private Handle buildBootstrapMethodHandle(BootstrapMethod bootstrapMethod) { + String className = Type.getInternalName(bootstrapMethod.enclosingType()); + String methodName = bootstrapMethod.name(); String methodType = MethodType.methodType( - methodHandle.returnType(), methodHandle.argumentTypes()) + bootstrapMethod.returnType(), + bootstrapMethod.parameterTypes()) .toMethodDescriptorString(); return new Handle( - handleKind, className, methodName, methodType, false /* itf */); + Opcodes.H_INVOKESTATIC, + className, + methodName, + methodType, + false /* itf */); } private Object decodeConstant(int index, Constant constant) { @@ -211,11 +152,10 @@ class IndyTransformer { } } - private Object[] buildBootstrapArguments(CalledByIndy callsite) { - Constant[] rawArgs = callsite.methodHandleExtraArgs(); - Object[] args = new Object[rawArgs.length]; - for (int i = 0; i < rawArgs.length; ++i) { - args[i] = decodeConstant(i, rawArgs[i]); + private Object[] buildBootstrapArguments(Constant[] bootstrapMethodArguments) { + Object[] args = new Object[bootstrapMethodArguments.length]; + for (int i = 0; i < bootstrapMethodArguments.length; ++i) { + args[i] = decodeConstant(i, bootstrapMethodArguments[i]); } return args; } @@ -237,8 +177,8 @@ class IndyTransformer { if (calledByIndy == null) { continue; } - if (calledByIndy.name() == null) { - throw new Error("CallByIndy annotation does not specify name"); + if (calledByIndy.fieldOrMethodName() == null) { + throw new Error("CallByIndy annotation does not specify a field or method name"); } final int PRIVATE_STATIC = Modifier.STATIC | Modifier.PRIVATE; if ((m.getModifiers() & PRIVATE_STATIC) != PRIVATE_STATIC) { |