Merge "Revert "Revert "Full-stack integrity: check vdex contents."""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 388ff06..6ad4eec 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -184,6 +184,7 @@
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 @@
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 @@
$$(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 @@
# (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 8a604db..bd3a145 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -936,13 +936,13 @@
// 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 012100d..71e276d 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -121,6 +121,7 @@
"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 f91f6e3..41133a8 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1676,10 +1676,10 @@
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 @@
// 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 @@
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 @@
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 0000000..f125222
--- /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 e9cc922..d054ece 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 d0f05d9..d4bed6b 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -66,14 +66,15 @@
// 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 @@
enum Mode {
kModeOat,
+ kModeOatWithBootImage,
kModeArt,
kModeSymbolize,
};
@@ -95,13 +97,56 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 a02cdc3..0000000
--- 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
deleted file mode 100644
index 6bc04e3..0000000
--- a/test/952-invoke-custom-kinds/classes/Main.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class
deleted file mode 100644
index 5dfe958..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class
deleted file mode 100644
index a11ee69..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class
deleted file mode 100644
index e233feb..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class
deleted file mode 100644
index 41e1d43..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
deleted file mode 100644
index b8dcd55..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
deleted file mode 100644
index 7906f99..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class
deleted file mode 100644
index c3266e4..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
deleted file mode 100644
index 03dc233..0000000
--- a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-kinds/expected.txt b/test/952-invoke-custom-kinds/expected.txt
deleted file mode 100644
index c41b5c6..0000000
--- 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 33b4cff..0000000
--- 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 be01c45..767cc7e 100644
--- a/test/952-invoke-custom/expected.txt
+++ b/test/952-invoke-custom/expected.txt
@@ -13,3 +13,8 @@
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 2e1db82..0b1c1ff 100644
--- a/test/952-invoke-custom/src/Main.java
+++ b/test/952-invoke-custom/src/Main.java
@@ -86,5 +86,6 @@
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 0000000..7b88c18
--- /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 761d182..2ef7ff7 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 @@
}
@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 74ac3cd..ff598bb 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 @@
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 acb6986..0015c00 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 @@
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 @@
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 @@
@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 3a63b33..139a172 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 @@
},
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 @@
}
@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 @@
},
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
similarity index 65%
rename from test/952-invoke-custom/src/annotations/LinkerMethodHandle.java
rename to test/952-invoke-custom/src/annotations/BootstrapMethod.java
index e0e56c5..c167830 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.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 17b8259..c4d13a2 100644
--- a/test/952-invoke-custom/src/annotations/CalledByIndy.java
+++ b/test/952-invoke-custom/src/annotations/CalledByIndy.java
@@ -28,15 +28,17 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CalledByIndy {
- LinkerMethodHandle[] invokeMethodHandle() default {};
+ /** Resolver metadata for bootstrapping */
+ BootstrapMethod[] bootstrapMethod() default {};
- LinkerFieldHandle[] fieldMethodHandle() default {};
+ /** Field or method name. */
+ String fieldOrMethodName();
- String name();
-
+ /** 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 a3efe24..0000000
--- 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 5847e2f..0000000
--- 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 286c098..45cb476 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 @@
/**
* 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 @@
* 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 @@
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 @@
}
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 @@
}
}
- 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 @@
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) {